(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
	typeof define === 'function' && define.amd ? define(['exports'], factory) :
	(factory((global.GSThree = global.GSThree || {})));
}(this, (function (exports) { 'use strict';

/**
 * Created by scenic on 2017/8/2.
 */


Array.prototype.deleteElementByValue = function (varElement) {
    var numDeleteIndex = -1;
    for (var i = 0; i < this.length; i++) {
        // 严格比较，即类型与数值必须同时相等。
        if (this[i] === varElement) {
            this.splice(i, 1);
            numDeleteIndex = i;
            break;
        }
    }
    return numDeleteIndex;
};
Array.prototype.deleteByIndex = function (index) {
    return this.splice(index, 1);
};
Array.prototype.contains = function (vale) {
    for (var i = 0; i < this.length; i++) {
        if (this[i] === vale) {
            return true;
        }
    }
    return false;
};
Array.prototype.deleteAll = function () {
    this.splice(0, this.length);
};

/**
 * Created by scenic on 2017/8/3.
 */
function Vector3(x, y, z) {
    THREE.Vector3.call(this, x, y, z);
}
Vector3.prototype = Object.assign(Object.create(THREE.Vector3.prototype), {
    constructor: Vector3,
});


function Triangle(a, b, c) {
    THREE.Triangle.call(this, a, b, c);
}
Triangle.prototype = Object.assign(Object.create(THREE.Triangle.prototype), {
    constructor: Triangle,
    pointIsSelfVertices: function (point) {
        var a = this.a, b = this.b, c = this.c;
        if (a.equalsFloat(point) || b.equalsFloat(point) || c.equalsFloat(point)) {
            return true;
        }
        return false;
    },
    isPointInEdges: function (point) {
        //判断点是否再三角形的边缘上的
        var a = this.a, b = this.b, c = this.c;
        var ab = new Vector3().subVectors(b, a).normalize();
        var ac = new Vector3().subVectors(c, a).normalize();
        var bc = new Vector3().subVectors(c, b).normalize();
        var ap = new Vector3().subVectors(point, a).normalize();
        var bp = new Vector3().subVectors(point, b).normalize();
        if (ab.equalsFloat(ap) || ac.equalsFloat(ap) || bc.equalsFloat(bp)) {
            return true;
        }

        return false;
    }
});


function Box3(min, max) {
    THREE.Box3.call(this, min, max);
}
Box3.prototype = Object.assign(Object.create(THREE.Box3.prototype), {
    constructor: Box3,
});

function Face3(a, b, c, normal, color, materialIndex) {
    THREE.Face3.call(this, a, b, c, normal, color, materialIndex);
}
Face3.prototype = Object.assign(Object.create(THREE.Face3.prototype), {constructor: Face3});

function Line3(start, end) {
    THREE.Line3.call(this, start, end);
}
Line3.prototype = Object.assign(Object.create(THREE.Line3.prototype), {
    constructor: Line3,

});

function Plane(normal, constant ) {
    THREE.Plane.call(this, normal, constant);
}
Plane.prototype = Object.assign(Object.create(THREE.Plane.prototype), {constructor: Plane});

function Vector2(x, y) {
    THREE.Vector2.call(this,x,y);
}
Vector2.prototype = Object.assign(Object.create(THREE.Vector2.prototype),{
    constructor:Vector2
});


function Quaternion(x,y,z,w) {
    THREE.Quaternion.call(this,x,y,z,w);
}

Quaternion.prototype = Object.assign(Object.create(THREE.Quaternion.prototype),{constructor:Quaternion});


function Matrix4() {
    THREE.Matrix4.call(this);
}

Matrix4.prototype = Object.assign(Object.create(THREE.Matrix4.prototype),{constructor:Matrix4});

/**
 * Created by scenic on 2017/8/3.
 */


var CommonUtils$1 = {
    notNull: function (value) {
        if (value == undefined || value == null) {
            throw new Error("the value is undefined")
        }

        return value;
    },
    parseProportion: function (string) {
        //like 1:1:1:1
        var allRate = string.split(":");
        // console.log(allRate);
        var sum = 0;
        allRate.forEach(function (value) {
            sum += parseFloat(value);
        });
        // console.log("sum is " + sum);
        var array = [];
        allRate.forEach(function (value) {
            array.push(value / sum);
        });
        return array;
    },
    isParamFunction: function (object) {
        if (typeof object != "function") {
            throw new Error("it need a function param")
        }
        return object;
    },
    object2String: function (object, _split) {
        var output = "";
        var split = _split != undefined ? _split : ",";
        for (var i in object) {
            var property = object[i];
            if (typeof property == "function") {
                continue;
            }

            if (typeof property == "number") {
                var regex = /\d+\.\d+/;
                if (regex.test(property)) {
                    //判断是否是float 数据格式
                    property = parseFloat(property).toFixed(2);
                }
            }
            output += i + "=" + property + split;
        }
        return output;
    },

    checkObjectField: function (array) {
        for (var i = 0; i < array.length; i++) {
            var temp = array[i];
            if (temp == undefined) {
                throw new Error("Error the json has not the field index: " + i);
            }
        }
    },
    showDialog: function (message, width, dialogID) {
        dialogID = dialogID == undefined ? "scene-dialog" : dialogID;
        var dialog = document.getElementById(dialogID);
        if (dialog == null) {
            dialog = document.createElement("div");
            dialog.id = dialogID;
            dialog.style.zIndex = 10000;
            dialog.innerHTML = "null";
            document.body.appendChild(dialog);
        }

        if (dialog.innerHTML == "null") {
            //first init the dialog

            $("#" + dialogID).dialog(
                {
                    width: CommonUtils$1.available(width) ? width : 500,
                    position: {my: "center", at: "left-800px top-500px ", of: window}
                }
            );

        } else {
            $("#" + dialogID).dialog();
        }

        dialog.innerHTML = message;

        return dialog;
    },
    createTableHTMLS: function (lines) {
        var message = "<table border='0' cellpadding='5'>";
        for (var i = 0; i < lines.length; i++) {
            message += "<tr>";
            for (var j = 0; j < lines[i].length; j++) {
                message += "<td>";
                message += lines[i][j];
                message += "</td>";
            }
            message += "</tr>";
        }
        message += "</table>";
        return message;
    },

    showDialogWithTable: function (lines, width) {
        showDialog(createTableHTMLS(lines), width);
    },
    /**
     * 获取函数名称
     * @param {Function} func 函数引用
     * @return {String} 函数名称
     */
    getFunctionName: function (func) {
        if (typeof func == 'function' || typeof func == 'object') {
            var name = ('' + func).match(/function\s*([\w\$]*)\s*\(/);
        }
        return name && name[1];
    },


    toastShow: (function toastShow() {
        var toastContainer = undefined;
        var toastTime = null;
        var displayTime = null;

        

    })(),
    loadDB: function () {
        var store = window.localStorage, doc = document.documentElement;
        if (!store) {
            doc.style.behavior = 'url(#default#userData)';
        }
        return {
            /**
             * 保存数据
             */
            set: function (key, val, context) {
                if (store) {
                    return store.setItem(key, val, context);
                } else {
                    doc.setAttribute(key, value);
                    return doc.save(context || 'default');
                }
            },
            /**
             * 读取数据
             */
            get: function (key, context) {
                if (store) {
                    return store.getItem(key, context);
                } else {
                    doc.load(context || 'default');
                    return doc.getAttribute(key) || '';
                }
            },
            /**
             * 删除数据
             * @param {Object}
             * @param {Object}
             */
            rm: function (key, context) {
                if (store) {
                    return store.removeItem(key, context);
                } else {
                    context = context || 'default';
                    doc.load(context);
                    doc.removeAttribute(key);
                    return doc.save(context);
                }
            },
            /**
             * 清空数据
             */
            clear: function () {
                if (store) {
                    return store.clear();
                } else {
                    doc.expires = -1;
                }
            }
        };
    }(),
    doSave: function (value, name) {
        var type = "text/latex";
        var blob;
        if (typeof window.Blob == "function") {
            blob = new Blob([value], {type: type});
        } else {
            var BlobBuilder = window.BlobBuilder || window.MozBlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder;
            var bb = new BlobBuilder();
            bb.append(value);
            blob = bb.getBlob(type);
        }
        var URL = window.URL || window.webkitURL;
        var bloburl = URL.createObjectURL(blob);
        var anchor = document.createElement("a");
        if ('download' in anchor) {
            anchor.style.visibility = "hidden";
            anchor.href = bloburl;
            anchor.download = name;
            document.body.appendChild(anchor);
            var evt = document.createEvent("MouseEvents");
            evt.initEvent("click", true, true);
            anchor.dispatchEvent(evt);
            document.body.removeChild(anchor);
        } else if (navigator.msSaveBlob) {
            navigator.msSaveBlob(blob, name);
        } else {
            location.href = bloburl;
        }
    },

    /**
     * 创建一个slider控件
     * @param title
     * @param uiUpdateCallback
     * @param config
     * @returns {Element}
     */
    createSliderView: function (title, uiUpdateCallback, config) {
        config = config || {};

        var div = document.createElement("div");
        div.style.padding = "10px 10px 10px 10px";
        div.style.border = "1px solid rgb(0, 0, 0)";
        div.innerHTML = "<span>" + title + "&nbsp" + "</span>";

        //input slider
        var editText = document.createElement("input");
        editText.setAttribute("type", "text");
        editText.value = config.value;
        editText.onkeydown = function (keyEvent) {
            if (keyEvent.key == "Enter") {
                $(slider).slider("value", editText.value);
                if (uiUpdateCallback) {
                    uiUpdateCallback(editText.value);
                }
            }

        };
        editText.onblur = function (event) {
            $(slider).slider("value", editText.value);
            if (uiUpdateCallback) {
                uiUpdateCallback(editText.value);
            }
        };
        div.appendChild(editText);

        if (config.showSlider !== false) {
            var slider = document.createElement("div");
            slider.style.marginLeft = "10px";
            $(slider).slider({
                max: config.max,
                min: config.min,
                value: config.value,
                slide: function (event, ui) {
                    $(editText)[0].value = ( ui.value );
                    if (uiUpdateCallback) {
                        uiUpdateCallback(ui.value);
                    }
                }
            });
            div.appendChild(slider);
        }

        return div;
    },
    /**
     * remove all the children come from the div
     * @param div
     */
    deleteDivElement: function (div) {
        while (div.hasChildNodes()) //当div下还存在子节点时 循环继续
        {
            div.removeChild(div.firstChild);
        }
    }
    /**
     * it return true if the object is function
     * @param object
     * @returns {*}
     */

};

/**
 * Created by scenic on 2017/8/2.
 */
function ListenerExecute() {
    this.listenerArray = [];
}
ListenerExecute.prototype = Object.assign({
    constructor: ListenerExecute,
    addListener: function (listener, alias) {
        if (typeof listener != "function") {
            throw new Error("listener must be a function")
        }

        if (this.listenerArray.contains(listener)) {
            this.removeListener(listener);
        }
        if (typeof alias != "undefined") {
            listener.alias = alias;
        }

        this.listenerArray.push(listener);
    },
    /**
     * @param listener  回掉函数或者函数的别名
     */
    removeListener: function (listener) {
        var objectListener = listener;

        if (typeof listener == "string") {
            //可能是别名

            for (var i = 0; i < this.listenerArray.length; i++) {
                var tmp = this.listenerArray[i];
                if (tmp.alias == listener) {
                    objectListener = tmp;
                    break;
                }
            }
        }

        if (objectListener != null && typeof objectListener == "function"){
            this.listenerArray.deleteElementByValue(CommonUtils$1.isParamFunction(objectListener));
        }
    },

    forEach: function (fun) {
        return this.listenerArray.forEach(fun);
    }
});

/**
 * Created by scenic on 2017/8/2.
 */
function EdgesHelper(geometry, associateMesh) {
    THREE.LineSegments.call(this, new THREE.EdgesGeometry(geometry), new THREE.LineBasicMaterial({color: this.defaultColor}));
    this.name = "helper-line";
    this.raycastSensitivity = false;
    this.associateMesh = associateMesh;
    this.visible = true;
}

EdgesHelper.prototype = Object.assign(Object.create(THREE.LineSegments.prototype), {
    constructor: EdgesHelper,
    defaultColor: "#95948e",

    setColor: function (color) {
        this.material.color.set(color);
    }

});

/**
 * Created by scenic on 2017/8/2.
 */
var Config = {
    //决定哪些种类的物体不显示再资源管理器中
    ResourceFilter: [
        THREE.TransformControls,
        THREE.AxisHelper,
        THREE.GridHelper,
        THREE.DirectionalLightHelper,
        EdgesHelper,
    ],

    defaultGetTransformControls : function(camera, domElement) {
    var translate = new THREE.TransformControls(camera, domElement);
    translate.setTranslationSnap(null);
    translate.setSize(0.5);
    translate.setSpace("local");
    translate.raycastSensitivity = false;
    return translate;
}
};

/**
 * Created by scenic on 2017/8/2.
 */

var id = 0;

function createID() {
    return ++id;
}

function File(object) {
    this.id = createID();
    this.userData = {object: object};

    this.start = "<li id='{0}'><span id='{0}' class='file'>{1}</span>".format(this.id, this.id, object.name != undefined ? object.name : "undefined");

    // console.log("<li obj_id = '" + id + "'>" +
    //     "<span obj_id='" + id + "' class='file'>" +
    //     name +
    //     "</span>")

    this.end = "</li>";

    this.name = "file:　" + object.name;
}

File.prototype = {
    constructor: File,
    traverse: function (callback) {
        callback(this);
    },

    toString: function () {
        return this.start + this.end;
    }
};

function Folder(object) {
    this.id = createID();
    this.files = [];
    this.start = undefined;
    this.end = undefined;
    this.userData = {};
    this.set(object);
}

Folder.prototype = {
    constructor: Folder,
    set: function (object) {
        if (!(object instanceof Object)) {
            throw new Error("the param should be Object container the name and id");
        }

        //start里边的id 必须和 本身的ID统一起来
        this.start = "<li id='{0}'><span id='{0}' class='folder'>{2}</span>".format(this.id, this.id, object.name != undefined ? object.name : "undefined");
        this.end = "</li>";
        this.userData.object = object;
        this.name = "folder:　" + object.name;
        // "<li obj_id = '" + id + "'>" +
        // "<span obj_id='" + id + "' class='folder'>" +
        // name +
        // "</span>";
    },
    add: function (object) {
        if (object instanceof File || object instanceof Folder) {
            this.files.push(object);
            object.parent = this;
        } else {
            var file = new File(object);
            file.parent = this;
            this.files.push(file);
        }
    },
    traverse: function (callback) {
        callback(this);

        var children = this.files;

        for (var i = 0; i < children.length; i++) {
            children[i].traverse(callback);
        }
    },
    clear: function () {
        this.files.deleteAll();
    },
    copy: function (folder) {
        // this.files.deleteAll();
        // folder.files.forEach(function (file) {
        //     this.files.push(file)
        // })

        this.files = folder.files;
        this.userData = folder.userData;
        this.set(folder.userData.object);
    },
    toString: function () {
        var start = this.start;
        for (var i = 0; i < this.files.length; i++) {
            var f = this.files[i];
            start += "<ul>" + f.toString() + "</ul>";
        }
        return start + this.end;
    }
};

/**
 * Created by scenic on 2017/8/2.
 */
function HighlightHelper() {
    this.object = undefined;
    var hoverLine;
    var selectedLine;
    var scope = this;

    function createLingSegment(color) {
        return new THREE.LineSegments(new THREE.BufferGeometry(), new THREE.LineBasicMaterial({
            color: color,
            linewidth: 2,
            transparent: false,
            depthTest: false,
            depthWrite: false
        }));
    }

    function attach(object, lineSegment) {
        scope.object = object;
        if (object && object.edgesHelper) {
            var scene = object.edgesHelper.parent;
            if (scene) {
                var position = object.edgesHelper.geometry.attributes.position;
                scene.add(lineSegment);

                lineSegment.geometry.removeAttribute('position');
                lineSegment.geometry.addAttribute('position', position);
                lineSegment.synchronizeTranslate(object);
                lineSegment.visible = true;
            }
        } else {
            lineSegment.visible = false;
        }
    }

    this.attachHover = function (object) {
        if (hoverLine == undefined) {
            hoverLine = createLingSegment("#ff08fd");
        }
        attach(object, hoverLine);
    };

    this.attachSelected = function (object) {
        if (selectedLine == undefined) {
            selectedLine = createLingSegment("#ff9100");
        }
        attach(object, selectedLine);
    };
}

/**
 * Created by scenic on 2017/8/2.
 */
// import {Config} from 'Constant'
var Utils = {
    getCurrentViewport: function (event) {
        var element = event.currentTarget;
        if (element instanceof HTMLElement) {
            var view = element.wrapper;
            return view;
        } else {
            console.error("the event not a mouse event");
        }
    },

    fillFolderWithScene: function (scene, rootFolder) {
        if (rootFolder == undefined) {
            throw new Error("the rootFolder is required")
        }


        function reclusiveSet(object, folder) {

            var children = object.children;

            for (var i = 0; i < Config.ResourceFilter.length; i++) {
                if (object instanceof Config.ResourceFilter[i]) {
                    return;
                }
            }

            if (children.length <= 0) {
                var file = new File(object);
                folder.add(file);
                return folder;
            } else {
                var newFolder = new Folder(object);
                folder.add(newFolder);

                for (var i = 0; i < children.length; i++) {
                    var child = children[i];
                    reclusiveSet(child, newFolder);
                }

                return folder;
            }

        }

        reclusiveSet(scene, rootFolder);

        return rootFolder
    },

    showSceneResource: (function () {
        var listener = new ListenerExecute();
        return function showSceneResource(scene, divID) {
            var rootFolder = new Folder({name: "root"});
            rootFolder.add(scene.viewport.camera);
            rootFolder.add(scene.viewport.control);

            Utils.fillFolderWithScene(scene, rootFolder);
            var str = "<ul id='browser' class='filetree'>" + rootFolder.toString() + "</ul>";
            var div;

            if (divID == undefined) {
                div = CommonUtils$1.showDialog(str, 300);
            } else {
                div = document.getElementById(divID);
                div.innerHTML = str;

            }
            var selectedHelper = new HighlightHelper();
            $("#browser").treeview();
            div.addEventListener('click', function (e) {
                console.log(e.target);
                var id = e.target.attributes['id'].value;
                rootFolder.traverse(function (folder) {
                    if (folder.id == parseFloat(id)) {
                        console.log(folder);
                        var object = folder.userData.object;
                        scene.attachTranslate(object);
                        selectedHelper.attachHover(object);

                        listener.forEach(function (l) {
                            l.call(l, folder);
                        });
                    }
                });

            });
            return listener;
        }

    })(),

    getCenterAroundObjects: (function () {
        var directionVectors = [
            //上
            new Vector3(0, 1, 0),
            //下
            new Vector3(0, -1, 0),
            //右
            new Vector3(1, 0, 0),
            //左
            new Vector3(-1, 0, 0),
            //前
            new Vector3(0, 0, 1),
            //后
            new Vector3(0, 0, -1),
        ];
        var rayCaster = new THREE.Raycaster();
        return function (centerObject, collidesCandidates) {
            //TODO(liuzd) collidesCandidates need to be set default value
            if (collidesCandidates == undefined) {
                collidesCandidates = [];
            }

            if (!(centerObject instanceof THREE.Mesh)) {
                console.error("the center-object request a Mesh type");
            }

            var aroundObjects = {
                all: []
            };
            directionVectors.forEach(function (direction) {
                var ray = rayCaster.ray;
                centerObject.updateMatrixWorld(true);
                ray.origin.setFromMatrixPosition(centerObject.matrixWorld);
                ray.direction.copy(direction);
                var intersects = rayCaster.intersectScene(collidesCandidates, false);
                if (intersects.length > 0) {
                    aroundObjects.all.push(intersects);
                    if (direction == directionVectors[0]) {
                        //up
                        aroundObjects.up = intersects;
                    }
                    else if (direction == directionVectors[1]) {
                        //down
                        aroundObjects.down = intersects;
                    }
                    else if (direction == directionVectors[2]) {
                        //right
                        aroundObjects.right = intersects;
                    }
                    else if (direction == directionVectors[3]) {
                        //left
                        aroundObjects.left = intersects;
                    }
                    else if (direction == directionVectors[4]) {
                        //forward
                        aroundObjects.forward = intersects;
                    }
                    else if (direction == directionVectors[5]) {
                        //back
                        aroundObjects.back = intersects;
                    }
                }

            });
            return aroundObjects;
        }

    })(),

    /**
     *  计算木板的长宽高和三个坐标轴之间的关系,
     * @param object
     * @returns {{x: *, y: *, z: *}}  x, y ,z的值代表经过旋转之后，指向的一个方向
     */
    calcDirectionVector: (function () {

        var matrix = new THREE.Matrix4();
        var euler = new THREE.Euler();
        var target;

        function createTarget() {
            function createGroup(name) {
                var g = new THREE.Group();
                g.name = name;
                return g;
            }

            if (target == undefined) {
                //create three vector that mapping the three axis
                var rootName = "for-calc-direction-vector";
                target = createGroup(rootName);
                var c1 = createGroup("zero");
                var cx = createGroup("x");
                var cy = createGroup("y");
                var cz = createGroup("z");
                cx.position.set(1, 0, 0);
                cy.position.set(0, 1, 0);
                cz.position.set(0, 0, 1);
                target.add(c1);
                target.add(cx);
                target.add(cy);
                target.add(cz);
            }
            return target;


        }

        return function calcDirectionVector(object, scene, camera, renderer) {
            if (target == undefined) {
                createTarget();
                scene.add(target);
            }

            object.updateMatrixWorld(true);
            //make the target have the same rotation angle as the object
            matrix.identity().extractRotation(object.matrixWorld);
            target.quaternion.setFromEuler(euler.setFromRotationMatrix(matrix));
            renderer.render(scene, camera);

            //calculate the direction-vector each direction
            var zeroObj = target.getObjectByName("zero");
            var xObj = target.getObjectByName("x");
            var yObj = target.getObjectByName("y");
            var zObj = target.getObjectByName("z");
            var zeroPosition = new THREE.Vector3().setFromMatrixPosition(zeroObj.matrixWorld);
            var xPosition = new THREE.Vector3().setFromMatrixPosition(xObj.matrixWorld);
            var yPosition = new THREE.Vector3().setFromMatrixPosition(yObj.matrixWorld);
            var zPosition = new THREE.Vector3().setFromMatrixPosition(zObj.matrixWorld);
            var directionX = new THREE.Vector3().subVectors(xPosition, zeroPosition).normalize();
            var directionY = new THREE.Vector3().subVectors(yPosition, zeroPosition).normalize();
            var directionZ = new THREE.Vector3().subVectors(zPosition, zeroPosition).normalize();
            return {xAxisPointTo: directionX, yAxisPointTo: directionY, zAxisPointTo: directionZ};
        };


    })(),


    intersects: (function () {
        var box1 = new THREE.Box3();
        var box2 = new THREE.Box3();

        return function (object1, object2) {
            box1.setFromObject(object1);
            box2.setFromObject(object2);
            return box1.intersectsBox(box2)
        }
    })(),

    exportObject: function (object) {
        var exporter = new THREE.OBJExporter();
        var result = exporter.parse(object);
        return result;
    },

    loadJsModel: function (jsPath, callback_) {
        var callback;
        if (typeof callback_ == "undefined") {
            callback = function (geometry, materials) {

                if (LogSwitch.open) {
                    console.log("materials is  " + materials);
                }
                // var mesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
                var mesh = new THREE.Mesh(geometry, new THREE.MultiMaterial(materials));
                mesh.name = "loadJsModel";

            };
        } else {
            callback = callback_;
        }

        var jsonLoader = new THREE.JSONLoader();
        jsonLoader.load(jsPath, callback);
    },

    loadObjModel: function (objModelPath, callBack) {
        var rootObjPath = objModelPath;
        // var rootObjPath = "/3d_models/obj/"
        var modelName;
        if (/(.+\/)(.+)\.obj/.test(objModelPath)) {
            rootObjPath = RegExp.$1;
            modelName = RegExp.$2;
            console.log(rootObjPath + "\t\t" + modelName);
        }

        var onProgress = function (xhr) {
            if (xhr.lengthComputable) {
                var percentComplete = xhr.loaded / xhr.total * 100;
                console.log("the percent is " + Math.round(percentComplete, 2) + " % downloaded");
            }
        };
        var onError = function (xhr) {
        };
        var mtlLoader = new THREE.MTLLoader();
        mtlLoader.setBaseUrl(rootObjPath);
        mtlLoader.setPath(rootObjPath);
        mtlLoader.load(modelName + ".mtl", function (materials) {
            console.log("materialsInfo is : " + materials);
            materials.preload();
            var objLoader = new THREE.OBJLoader();
            objLoader.setMaterials(materials);
            objLoader.setPath(rootObjPath);
            objLoader.load(modelName + ".obj", function (mesh) {

                // obj.scale.set(0.5,0.5,0.5);
                // MarchAPI.sceneEntity.add(obj);
                callBack(mesh);
            }, onProgress, onError);
        });

    },

    autoAdjustCameraPosition: function (camera, width, height, depth) {
        //visible_height = 2 * Math.tan( ( Math.PI / 180 ) * camera.fov / 2 ) * distance_from_camera;

        var length = Math.sqrt(width * width + height * height + depth * depth);
        console.log("length is " + length);
        var degrees = camera.fov / camera.zoom / 2;
        console.log(degrees);

        camera.position.z = length / 2 / Math.sin(degrees * Math.PI / 180);
        console.log(camera.position.z);
        camera.updateProjectionMatrix();
    },

    convertTo3DCoordinate: function (mousePosition, camera) {
        return new THREE.Vector3(mousePosition.x, mousePosition.y, 0.5).unproject(camera);
    },

    loadTexture: function (materialImgPath, callback) {

        var texture = new THREE.TextureLoader().load(materialImgPath, callback);
        texture.wrapT = texture.wrapS = THREE.RepeatWrapping;
        // texture.wrapT = texture.wrapS = THREE.ClampToEdgeWrapping;
        // texture.wrapT = texture.wrapS = THREE.MirroredRepeatWrapping;
        return texture;
    },

    transparentMesh: function (mesh, isTransparent, isClearCacheData) {
        var material = mesh.material;

        if (isTransparent) {
            if (mesh.backupMatrials) {
                if (isClearCacheData) {
                    mesh.backupMatrials.deleteAll();
                }

            } else {
                mesh.backupMatrials = [];
            }


            if (material instanceof Array) {
                for (var i = 0; i < material.length; i++) {
                    mesh.backupMatrials.push({
                        opacity: material[i].opacity,
                        transparent: material[i].transparent
                    });
                    material[i].transparent = true;
                    material[i].opacity = 0;
                }
            } else {
                mesh.backupMatrials.push({
                    opacity: material.opacity,
                    transparent: material.transparent
                });
                material.transparent = true;
                material.opacity = 0;
            }

        } else {
            var backData = mesh.backupMatrials;
            if (material instanceof Array) {
                for (var i = 0; i < material.length; i++) {
                    material[i].opacity = backData[i].opacity;
                    material[i].transparent = backData[i].transparent;
                }
            } else {
                material.opacity = backData[0].opacity;
                material.transparent = backData[0].transparent;
            }
        }

    },

    showEdgesHelper: function (scene, start, show) {
        console.info("showEdgesHelper " + show);
        for (var i = 0; i < scene.children.length; i++) {
            var children = scene.children[i];
            if (children instanceof EdgesHelper) {
                var helperMesh = children.associateMesh;
                if (helperMesh.visible) {
                    //住的Mesh显示的情况下才做处理
                    children.visible = true;
                    if (show) {
                        //mesh设置为透明的
                        Utils.transparentMesh(helperMesh, true);
                        // backupDataAndTransparent(helperMesh.material);
                    } else {
                        //恢复mesh的透明度
                        // reload(helperMesh.material);
                        Utils.transparentMesh(helperMesh, false);
                    }

                    // function backupDataAndTransparent(material) {
                    //     if (helperMesh.backupMatrials) {
                    //         helperMesh.backupMatrials.deleteAll();
                    //     }
                    //
                    //     helperMesh.backupMatrials = [];
                    //
                    //     if (material instanceof Array) {
                    //         for (var i = 0; i < material.length; i++) {
                    //             helperMesh.backupMatrials.push({
                    //                 opacity: material[i].opacity,
                    //                 transparent: material[i].transparent
                    //             })
                    //             material[i].transparent = true;
                    //             material[i].opacity = 0;
                    //         }
                    //     } else {
                    //         helperMesh.backupMatrials.push({
                    //             opacity: material.opacity,
                    //             transparent: material.transparent
                    //         })
                    //         material.transparent = true;
                    //         material.opacity = 0;
                    //     }
                    // }
                    //
                    // function reload(material) {
                    //     var backData = helperMesh.backupMatrials;
                    //     if (material instanceof Array) {
                    //         for (var i = 0; i < material.length; i++) {
                    //             material[i].opacity = backData[i].opacity;
                    //             material[i].transparent = backData[i].transparent;
                    //         }
                    //     } else {
                    //         material.opacity = backData[0].opacity;
                    //         material.transparent = backData[0].transparent;
                    //     }
                    // }
                }
            }
        }
    },

    matrix4ToStringTable: function (matrix4) {
        var elements = matrix4.elements;
        var lines = [];
        lines.push([elements[0], elements[4], elements[8], elements[12]]);
        lines.push([elements[1], elements[5], elements[9], elements[13]]);
        lines.push([elements[2], elements[6], elements[10], elements[14]]);
        lines.push([elements[3], elements[7], elements[11], elements[15]]);
        for (var i = 0; i < lines.length; i++) {
            for (var j = 0; j < lines[i].length; j++) {
                lines[i][j] = parseFloat(lines[i][j]).toFixed(2);
            }
        }

        return CommonUtils$1.createTableHTMLS(lines);
    },
    // var array = [];
    // scene.traverse(function (mesh) {
    //     if (mesh instanceof Mesh) {
    //         if (mesh.material && mesh.geometry) {
    //             array.push(mesh);
    //         }
    //     }
    // })
    // if (show) {
    //     array.forEach(function (mesh) {
    //         if (mesh.edgesHelper == undefined) {
    //             mesh.edgesHelper = new THREE.LineSegments(new THREE.EdgesGeometry(mesh.geometry), new THREE.LineBasicMaterial({color: Math.random() * 0xff0000}));
    //         }
    //         scene.add(mesh.edgesHelper);
    //         mesh.backupOpacity = mesh.material.opacity;
    //         mesh.backupTransparent = mesh.material.transparent;
    //         mesh.material.transparent = true;
    //         mesh.material.opacity = 0;
    //     })
    //     start.OnRePaintListener.addListener(repaintListener, "update-helper");
    // } else {
    //     array.forEach(function (mesh) {
    //         var helper = mesh.edgesHelper;
    //         scene.remove(helper);
    //         mesh.material.opacity = mesh.backupOpacity ;
    //         mesh.material.transparent = mesh.backupTransparent;
    //     })
    //
    //     start.OnRePaintListener.removeListener(repaintListener);
    //
    // }
    // array.deleteAll();

    removeDatGUIFolders: function (gui) {
        var controllers = gui.__controllers;
        var N = controllers.length;
        for (var i = 0; i < N; i++) {
            var control = controllers[i];
            gui.remove(control);
            controllers.deleteElementByValue(control);
            N--;
            i--;
        }
    },


    topView: function (scene) {
        var camera = scene.viewport.changeCamera('o');
        camera.position.set(0, 10000, 0);
        camera.rotation.set(-Math.PI / 2, 0, 0);
        scene.viewport.control.enableRotate = false;

        if (scene.gridHelper) {
            scene.gridHelper.rotation.set(0, 0, 0);
        }
    },
    frontView: function (scene) {
        var camera = scene.viewport.changeCamera('o');
        camera.position.set(0, 0, 10000);
        camera.rotation.set(0, 0, 0);
        scene.viewport.control.enableRotate = false;
        if (scene.gridHelper) {
            scene.gridHelper.rotation.set(Math.PI / 2, 0, 0);
        }
    },
    leftView: function (scene) {
        var camera = scene.viewport.changeCamera('o');
        camera.position.set(-10000, 0, 0);
        camera.rotation.set(0, -Math.PI / 2, 0);
        scene.viewport.control.enableRotate = false;
        if (scene.gridHelper) {
            scene.gridHelper.rotation.set(0, 0, Math.PI / 2);
        }
    },
    perspectiveView: function (scene) {
        scene.viewport.changeCamera('p');
        if (scene.gridHelper) {
            scene.gridHelper.rotation.set(0, 0, 0);
        }
    },


    debugDrawLine: (function () {
        var mesh;
        return function drawLine(scene, vectorArray, newMesh, color) {
            // if(newMesh === true || mesh == undefined) {
            //     mesh = createMesh(color);
            // }
            //
            // var index = 0;
            // var positions = mesh.geometry.attributes.position.array;
            // vectorArray.forEach(function (v) {
            //     positions[index] = v.x;
            //     positions[index + 1] = v.y;
            //     positions[index + 2] = v.z;
            //     index += 3;
            // });
            // scene.add(mesh);

            if (scene == undefined) {
                return;
            }


            var dir = vectorArray[1].clone().sub(vectorArray[0]).normalize();
            scene.add(new THREE.ArrowHelper(dir, vectorArray[0], vectorArray[1].clone().sub(vectorArray[0]).length(), color));

        }
    })(),
    debugShowPoints: (function () {

        var meshs = [];

        var color = [
            "#FF0000",
            "#00FF00",
            "#0000FF"
        ];
        return function (scene, points) {
            if (scene == undefined) {
                return;
            }
            for (var i = 0; i < meshs.length; i++) {
                scene.remove(meshs[i]);
                meshs.deleteByIndex(i);
                i--;
            }
            var colorIndex = 0;

            points.forEach(function (vector) {

                if (vector == undefined) {
                    throw new Error("The vector is undefined")
                }
                if (vector instanceof THREE.Vector2) {
                    vector = new THREE.Vector3(vector.x, vector.y, 0);
                }
                colorIndex %= color.length;
                var material = new THREE.MeshPhongMaterial({
                    color: color[colorIndex],
                    transparent: true,
                    opacity: 0.5
                });
                var mesh = new GSThree.Mesh(new THREE.SphereBufferGeometry(2), material);
                mesh.edgesHelper.setColor(color[colorIndex]);
                mesh.position.copy(vector);
                meshs.push(mesh);
                scene.add(mesh);
                colorIndex++;
            });

        }
    })(),


    // setMatrialMapRepeat:function (materials) {
    //     if(materials instanceof Array) {
    //         //左右封边
    //         [0, 1].forEach(function (index) {
    //
    //             if (materials[index].map) {
    //                 var constant = 1024.0;
    //                 checkRepeatAvailable(materials[index].map).repeat.set(size.z / constant, size.y / constant);
    //                 // materials[index].map.needsUpdate = true;
    //             }
    //         });
    //
    //         //上下贴皮
    //         [2, 3].forEach(function (index) {
    //             var constant = 1024.0;
    //             if (materials[index].map) {
    //                 var x = size.x / constant;
    //                 var y = size.z / constant;
    //                 console.log(this.direction);
    //                 if(
    //                     scope.direction == '90' ||
    //                     scope.direction == '-90'
    //                 ){
    //                     var tmp = x;
    //                     x = y;
    //                     y = tmp;
    //                 }
    //                 checkRepeatAvailable(materials[index].map).repeat.set(x, y);
    //             }
    //         });
    //
    //         //前后封边
    //         [4, 5].forEach(function (index) {
    //             var constant = 1024.0;
    //             if (materials[index].map) {
    //                 checkRepeatAvailable(materials[index].map).repeat.set(size.x / constant, size.y / constant);
    //             }
    //         });
    //     }
    // }


};

/**
 * Created by scenic on 2017/8/2.
 */

function Mesh(geometry, material) {
    THREE.Mesh.call(this, geometry, material);
    this.edgesHelper = new EdgesHelper(geometry, this);
}

Mesh.prototype = Object.assign(Object.create(THREE.Mesh.prototype), {
    constructor: Mesh,
    isGSMesh: true
});

/**
 * Created by scenic on 2017/8/5.
 */
var DebugDraw = {
    drawArrow: function (line3, color) {
        var scene = window.scene;
        if (scene == undefined) {
            console.warn("the scene is undefined");
            return;
        }

        var dir = line3.delta().normalize();
        var arrow = new THREE.ArrowHelper(dir, line3.start, line3.distance(), color);
        scene.add(arrow);
        return arrow;
    },
    drawPoints: (function () {

        var lastMeshArray = [];

        var color = [
            "#FF0000",
            "#00FF00",
            "#0000FF"
        ];
        return function (points) {
            var scene = window.scene;
            if (scene == undefined) {
                console.warn("the scene is undefined");
                return;
            }
            for (var i = 0; i < lastMeshArray.length; i++) {
                scene.remove(lastMeshArray[i]);
                lastMeshArray.deleteByIndex(i);
                i--;
            }
            var colorIndex = 0;

            points.forEach(function (vector) {

                if (vector == undefined) {
                    throw new Error("The vector is undefined")
                }
                if (vector.isVector2) {
                    vector = new Vector3(vector.x, vector.y, 0);
                }
                colorIndex %= color.length;
                var material = new THREE.MeshPhongMaterial({
                    color: color[colorIndex],
                    transparent: true,
                    opacity: 0.5
                });
                var mesh = new Mesh(new THREE.SphereBufferGeometry(2), material);
                mesh.edgesHelper.setColor(color[colorIndex]);
                mesh.position.copy(vector);
                lastMeshArray.push(mesh);
                scene.add(mesh);
                colorIndex++;
            });

        }
    })(),

};

/**
 * Created by scenic on 2017/8/3.
 */
var MathUtils = {
    /**
     * 如果点再轮廓的边缘上，也是认为不包含的
     */
    isContourContainsPoint: (function () {
        var pa = new Vector3(), pb = new Vector3(), ab = new Vector3(), ap = new Vector3();

        return function isContourContainerPoint(contour, point) {
            /*
             思路：如果包含，那么点和轮廓上的所有点的向量夹角之和是360度
             */
            var DEBUG = this.debug;

            if(DEBUG){
                DebugDraw.drawPoints([point].concat(contour));
            }

            var degreeSum = 0;
            for (var i = 0; i < contour.length; i++) {
                var a = contour[i];
                var b = contour[(i + 1) % contour.length];
                var p = point;
                //首先排除点和轮廓点重合，或者再轮廓的边缘上
                if (p.equalsFloat(a) || p.equalsFloat(b)) {
                    //重合认为不包含
                    if(DEBUG){
                        console.log("the point in the same position , return false");
                    }

                    return false;
                }

                pb.subVectors(b, p);
                ab.subVectors(b, a);
                ap.subVectors(p, a);
                if (DEBUG) {
                    console.log("ap length : " + ap.length() + " pb length " + pb.length() + "  ab length " + ab.length());
                    console.log("ap: ");
                    console.log(ap);
                    console.log("pb");
                    console.log(pb);
                }
                var isLengthEquals = MathUtils.floatEquals(ab.length(), (ap.length() + pb.length()));

                ap.normalize();
                pb.normalize();
                var directionEquals = ap.equalsFloat(pb);

                if (isLengthEquals && directionEquals) {
                    console.log("the isLengthEquals and directionEquals is true , return false");
                    return false;
                }

                pa.subVectors(a, p).normalize();
                //角度的余弦值
                var dot = MathUtils.clamp(pa.dot(pb),-1,1);
                var degree = Math.acos(dot);

                if (DEBUG) {
                    console.log(MathUtils.getDegree(dot));
                }

                degreeSum += degree;
            }

            var result  = MathUtils.floatEquals(degreeSum, 2 * Math.PI);
            if (DEBUG) {
                if(result){
                    console.log("the sum degree is 360, return true");
                }else {
                    console.log("the sum degree is " + degreeSum + "  return false");
                }
                console.log("\n\n");
            }
            return result;
        }
    })(),
    /**
     * 如果点再三角形的顶点，或者边缘上，也是认为不包含的
     */
    isTriangleContainsPoint: (function () {
        var triangle = new Triangle();
        return function isTriangleContainPoint(a, b, c, point) {
            triangle.set(a, b, c);
            if (triangle.isPointInEdges(point) || triangle.pointIsSelfVertices(point)) {
                return false;
            }
            return triangle.containsPoint(point);
        }
    })(),

    getNormal: (function () {
        var ab = new Vector3();
        var ac = new Vector3();

        return function (a, b, c, optionalTarget) {
            var result = optionalTarget || new Vector3();
            ab.subVectors(b, a);
            ac.subVectors(c, a);
            return result.copy(ab).cross(ac).normalize();
        }
    })(),
    /**
     * 判断两点组成的线段 是否 穿过一个轮廓，所有的点要在一个平面上
     * @param contour
     * @param lineStart
     * @param lineEnd
     */
    isLineIntersectContour: (function () {
        var lineCount = new Line3(), line = new Line3();
        return function (contour, lineStart, lineEnd, order) {
            var DEBUG = this.debug;
            if (!(contour instanceof Array)) {
                console.error(contour);
                throw new Error("the contour must is array");
            }
            if (!lineEnd.isVector3 || !lineEnd.isVector3) {
                console.error(lineStart);
                console.log(lineEnd);
                throw new Error("the lineStart and lineEnd must is Vector3");
            }

            if (DEBUG) {
                console.log("=========start========");
                console.log(contour);
                console.log(lineStart);
                console.log(lineEnd);
                console.log("=========end========");

                Utils.debugShowPoints(window.scene, [].concat(contour, [lineStart, lineEnd]));
                Utils.debugDrawLine(window.scene, [lineStart, lineEnd], true, "#FF0000");

            }

            //排除线段的两个端点是否再轮廓内部的情况
            if (MathUtils.isContourContainsPoint(contour, lineStart)) {
                if (DEBUG) {
                    console.log("lineStart 包含再轮廓内");
                }
                return true;
            }
            if (MathUtils.isContourContainsPoint(contour, lineEnd)) {
                if (DEBUG) {
                    console.log("lineEnd 包含再轮廓内");
                }
                return true;
            }

            line.set(lineStart, lineEnd);
            for (var i = 0; i < contour.length; i++) {
                var p1 = contour[i];
                var p2 = contour[(i + 1) % contour.length];
                lineCount.set(p1, p2);
                var result = lineCount.intersectLine(line, true);
                if (DEBUG) {
                    console.log(result);
                    console.log(lineCount);
                    console.log(line);
                    console.log("==================");
                }
                if (result) {
                    if (DEBUG) {
                        console.log(lineCount);
                        console.log(line);
                        console.log(order);
                        console.log("线段和轮廓边缘有交叉 ");
                    }

                    return true;
                }
            }
            return false;
        }
    })(),

    isLineIntersectContourByExtendLength: function (contour, lineStart, lineEnd) {
        //如果线段的端点分布在轮廓的两侧的话，需要进一步判断
        var firstPointToProject = undefined;
        var zero = new Vector3();
        var DEBUG = this.debug;
        var ab = new Vector3(), ap = new Vector3();
        for (var i = 0; i < contour.length; i++) {
            var point = contour[i];
            if (point.equals(a) || point.equals(b)) {
                if (DEBUG) {
                    console.log("skip the  point ");
                    console.log(point);
                }
                continue;
            }

            var p = point;
            //计算出点再直线方向的投影向量
            ab.subVectors(b, a);
            pb.subVectors(b, p);
            ap.subVectors(p, a);
            var project = ap.projectOnVector(ab).add(a);

            //根据投影向量，计算出点和直线的垂直方向的向量
            var p_Project = new Vector3().subVectors(project, p);

            if (p_Project.equals(zero)) {
                if (DEBUG) {
                    console.log("skip the  point ");
                    console.log(point);
                }
                continue;
            }
            Utils.debugDrawLine(window.scene, [point, project], true, "#FFFF00");
            if (firstPointToProject == undefined) {
                firstPointToProject = p_Project;
                if (DEBUG) {
                    console.log("first sum is " + firstPointToProject.length());
                    console.log("first pointToProject ");
                    console.log(p_Project);
                }

            } else {
                var sum = firstPointToProject.clone().add(p_Project);
                var dot = firstPointToProject.dot(p_Project);
                if (DEBUG) {
                    console.log("other sum is " + sum.length());
                    console.log("dot is " + dot);
                }
                if (dot < 0 || sum.length() <= firstPointToProject.length()) {
                    return true;
                }
            }
        }


        return false;
    },

    /**
     * 计算一个点到线段的垂直向量
     */
    perpendicularVector: (function () {
        var delta = new Vector3();
        return function (point, line, optionalTarget) {
            var DEBUG = this.debug;
            var ab = line.delta(delta);
            var result = optionalTarget || new Vector3();
            result.subVectors(point, line.start)
                .projectOnVector(ab).add(line.start)
                .sub(point);

            if (DEBUG) {
                DebugDraw.drawPoints([point, line.start, line.end]);
                DebugDraw.drawArrow(line, "#FF0000");
                DebugDraw.drawArrow(new Line3(point, result.clone().add(point)), "#00FF00");
            }
            return result;
        }
    })(),

    getLineIntersectPoint: (function () {
        var normal1 = new Vector3();
        var normal2 = new Vector3();
        var perpendicular = new Vector3();
        var ac = new Vector3();
        var ab = new Vector3();
        return function (line1, line2, optionalTarget) {
            var DEBUG = this.debug;
            CommonUtils$1.notNull(line1);
            CommonUtils$1.notNull(line2);
            if (DEBUG) {
                DebugDraw.drawArrow(line1, "#FF0000");
                DebugDraw.drawArrow(line2, "#00FF00");
            }

            var a = line1.start;
            var b = line1.end;

            //检测是否再一个平面
            var normal1 = MathUtils.getNormal(a, b, line2.start, normal1).positive();
            var normal2 = MathUtils.getNormal(a, b, line2.end, normal2).positive();
            if (!normal1.equalsFloat(normal2)) {
                if (DEBUG) {
                    console.log("not in same face");
                }
                return null;
            }

            //检测是否平行
            var dot = Math.abs(line1.delta().dot(line2.delta()));
            if (Math.abs(dot - 1) < 0.0000001) {
                if (DEBUG) {
                    console.log("two line is parallel");
                }
                return null;
            }

            //开始计算
            var result = optionalTarget || new Vector3();
            a = line1.start;
            b = line1.end;
            var c = MathUtils.perpendicularVector(a, line2, perpendicular).add(a);
            if (c.equals(a)) {
                result.copy(a);
            } else {
                ac.subVectors(c, a);
                ab.subVectors(b, a);
                var m = ac.length() / ac.normalize().dot(ab.normalize());
                result.copy(a).addScaledVector(ab.normalize(), m);
                result.copy(a).addScaledVector(ab.normalize(), m);
            }

            if (DEBUG) {
                DebugDraw.drawArrow(new Line3(new Vector3(), result), "#00FFFF");
            }

            return result;

        }
    })(),

    clamp: function (value, min, max) {

        return Math.max(min, Math.min(max, value));

    },

    getDegree: function (dot) {
        var degree = Math.acos(this.clamp(dot, -1, 1));
        return degree * 180 / Math.PI;
    },

    isPointInLine: (function () {
        var pa = new Vector3();
        var pb = new Vector3();

        return function (line, point) {
            var DEBUG = this.debug;
            var a = line.start;
            var b = line.end;
            var p = point;

            if (point.equals(a) || point.equals(b)) {
                return true;
            }

            pa.subVectors(a, p).normalize();
            pb.subVectors(b, p).normalize();

            var dot = this.clamp(pa.dot(pb), -1, 1);

            if (DEBUG) {
                console.log(pa);
                console.log(pb);
                console.log(this.getDegree(dot));
            }

            if (MathUtils.floatEquals(dot, -1)) {
                return true;
            }
            return false;
        }
    })(),

    /**
     * array1 是否包含array2
     * @param array1
     * @param array2
     */
    arrayContainsArray: function (array1, array2) {
        var e2 = array2[0];
        var index = 0;
        for (var i = 0; i < array1.length; i++) {
            if (e2 === array1[i]) {
                index = i;
                var findTheSame = true;
                for (var j = 0; j < array2.length; j++) {
                    if (array2[j] !== array1[i + j]) {
                        findTheSame = false;
                        break;
                    }
                }
                if (findTheSame) {
                    var result = [];
                    array2.forEach(function () {
                        result.push(index++);
                    });
                    return result;
                }
            }
        }
        return null;
    },

    floatEquals: function (a, b) {
        if (Math.abs(a - b) < 0.00000001) {
            return true;
        }
        return false;
    }

};

/**
 * Created by scenic on 2017/8/4.
 */
var GeometryUtils = {

    /**
     * 获取一个面的原始的顶点的索引，按照顺序排列
     * @param normal
     * @param g_faces
     * @param keepOrinNotChange 如果是true 则不会删除原来的face
     * @returns {Array}
     */
    pickFaceByNormal: function (normal, g_faces, deleteFromOrgin) {
        //删除原始faces中的目标面，然后保存起来
        var targetFaces = [];
        for (var i = 0; i < g_faces.length; i++) {
            var f = g_faces[i];
            if (f.normal.equals(normal)) {
                targetFaces.push(f);
                if (deleteFromOrgin === true) {
                    g_faces.deleteElementByValue(f);
                    i--;
                }
            }
        }
        return targetFaces;
    },

    getFaceVerticesIndex: function (faces, g_vertices) {
        var DEBUG = this.debug;
        var faceVerticesIndex = [];

        function isVerticesMerged() {
            //顶点经过优化的
            return (faces.length == 2 && faces[0].b == faces[1].a && faces[0].c == faces[1].c);
        }

        function addAndCheckDuplicate(index) {
            var isNeedAdd = true;
            //去重筛选
            faceVerticesIndex.forEach(function (i) {
                if (g_vertices[i].equals(g_vertices[index])) {
                    isNeedAdd = false;
                }
            });

            if (isNeedAdd) {
                faceVerticesIndex.push(index);
            }

        }

        //对多个面的定点进行逆时针排序，去掉多余的定点，合并成一个面
        if (isVerticesMerged()) {

            if (DEBUG) {
                console.log("the geometry is standard Box");
            }
            //应该是BoxGeometry
            var a = faces[0].a;
            var b = faces[0].b;
            var c = faces[0].c;
            var d = faces[1].b;
            //逆时针充填
            faceVerticesIndex.push(a, b, d, c);
        } else {
            if (DEBUG) {
                console.log("the geometry is extrude geometry");
            }
            faces.forEach(function (f) {
                addAndCheckDuplicate(f.a);
                addAndCheckDuplicate(f.b);
                addAndCheckDuplicate(f.c);
            });

        }
        return faceVerticesIndex;
    },

    //根据面的单位向量，对定点进行排序
    orderVerticesByNormal: function (aIndex, bIndex, cIndex, vertices, normal) {
        var DEBUG = this.debug;

        var t1 = [aIndex, bIndex, cIndex];
        var t2 = [aIndex, cIndex, bIndex];

        var t1Vertices = [];
        var t2Vertices = [];

        t1.forEach(function (index) {
            t1Vertices.push(vertices[index]);
        });
        t2.forEach(function (index) {
            t2Vertices.push(vertices[index]);
        });

        if (DEBUG) {
            // console.log(aIndex + " " + bIndex + " " + cIndex);
            // console.log("normal : ");
            // console.log(normal);
            // console.log(t1Vertices);
            // console.log(t2Vertices);
            // console.log("vertices : ");
            // console.log(vertices);
        }

        var normal1 = MathUtils.getNormal(t1Vertices[0], t1Vertices[1], t1Vertices[2]);
        var normal2 = MathUtils.getNormal(t2Vertices[0], t2Vertices[1], t2Vertices[2]);


        if (normal == undefined) {
            return [aIndex, bIndex, cIndex];
        } else if (normal.equalsFloat(normal1)) {
            return t1;
        } else if (normal.equalsFloat(normal2)) {
            return t2;
        } else {
            console.warn(normal);
            console.warn("not matcher the case");
            console.warn(normal1);
            console.warn(normal2);
            console.warn("=================");
            MathUtils.getNormal(t1Vertices[0], t1Vertices[1], t1Vertices[2]);
        }

        // if (!THREE.ShapeUtils.isClockWise([vertices[a], vertices[b], vertices[c]])) {
        //     return t1;
        // }
        // if (!THREE.ShapeUtils.isClockWise([vertices[a], vertices[c], vertices[b]])) {
        //     return t2;
        // }
    },

    /**
     * 镂空一个面
     * @param faceNormal
     * @param holeContourArray
     * @param g_faces
     * @param g_vertices
     */
    hollowFace: function (faceNormal, holeContourArray, g_faces, g_vertices) {
        var DEBUG = this.debug;
        if (!CommonUtils$1.notNull(faceNormal).isVector3) {
            new Error("you must tell me the face normal that will be holed");
        }
        if (!Array.isArray(CommonUtils$1.notNull(holeContourArray))) {
            new Error("holeContourArray must be contour array ");
        }

        if (DEBUG) {
            if (window.debugShowLineIntersect == undefined) {
                window.debugShowLineIntersect = [];
            }
            if (window.debugShowBeCover == undefined) {
                window.debugShowBeCover = [];
            }
            if (window.debugShowAvialbePoint == undefined) {
                window.debugShowAvialbePoint = [];
            }
            if (window.needCheckedContourIndex == undefined) {
                window.needCheckedContourIndex = [];
            }

            window.needCheckedContour = needCheckedContour;
        }
        var alreadyAddedFace = [];
        var needCheckedContour = [];

        //先获取面上所有顶点的索引
        var faceVerticesIndexArray = GeometryUtils.getFaceVerticesIndex(GeometryUtils.pickFaceByNormal(faceNormal, g_faces, true), g_vertices);

        //再获取打孔的顶点数据的索引
        var holeContourIndexArray = [];
        holeContourArray.forEach(function (holeContour) {
            holeContourIndexArray.push(GeometryUtils.addHolePointToVerticesInNotExist(holeContour, g_vertices));
            //需要检索是否被覆盖的区域
            needCheckedContour.push(holeContour);
        });

        console.log("faceVerticesIndexArray");
        console.log(faceVerticesIndexArray);
        console.log("holeContourIndexArray");
        console.log(holeContourIndexArray);


        function checkVerticesAvailable(a, b, c, orders) {
            if (!CommonUtils$1.notNull(a).isVector3 || !CommonUtils$1.notNull(b).isVector3 || !CommonUtils$1.notNull(c).isVector3) {
                console.error(a);
                console.error(b);
                console.error(c);
            }

            //检测三角mesh是否相交要打的洞
            for (var i = 0; i < needCheckedContour.length; i++) {
                var contour = needCheckedContour[i];
                if (MathUtils.isLineIntersectContour(contour, a, c, orders)) {
                    if (DEBUG) {
                        window.debugShowLineIntersect.push(
                            ["contour", contour, 'a', 'c', [a, c], "orders", orders]
                        );
                    }
                    return false;
                }
                if (MathUtils.isLineIntersectContour(contour, b, c, orders)) {
                    if (DEBUG) {
                        window.debugShowLineIntersect.push(["contour", contour, 'b', 'c', [b, c], "orders", orders]);
                    }
                    return false;
                }
                if (MathUtils.isLineIntersectContour(contour, a, b, orders)) {
                    if (DEBUG) {
                        window.debugShowLineIntersect.push(["contour", contour, 'a', 'b', [a, b], "orders", orders]);
                    }
                    return false;
                }

                //检测是否点在三角面是否盖住了要打的洞
                for (var j = 0; j < contour.length; j++) {
                    var p = contour[j];
                    if (MathUtils.isTriangleContainsPoint(a, b, c, p)) {
                        if (DEBUG) {
                            window.debugShowLineIntersect.push(["contour", contour, "points", [a, b, c, p], "orders", orders]);
                        }
                        return false;
                    }
                }
            }

            return true;
        }

        if (DEBUG) {
            var points = [];
            holeContourArray.forEach(function (holeContour) {
                holeContour.forEach(function (p) {
                    points.push(p);
                });
            });
            Utils.debugShowPoints(window.scene, points);
        }

        function addFaceToArray(order) {
            //增加到face数组
            //检测数组中是否存在了
            var canAddToArray = true;

            for (var i = 0; i < alreadyAddedFace.length; i++) {
                var lastOrder = alreadyAddedFace[i];

                var a = order[0], b = order[1], c = order[2];
                var a1 = lastOrder[0], b1 = lastOrder[1], c1 = lastOrder[2];
                if (DEBUG) {
                    console.log(lastOrder);
                }


                if (a == a1 && b == b1 && c == c1) {
                    canAddToArray = false;
                    break;
                }
            }

            if (canAddToArray) {
                needCheckedContour.push([g_vertices[order[0]], g_vertices[order[1]], g_vertices[order[2]]]);
                g_faces.push(new Face3(order[0], order[1], order[2]));
                alreadyAddedFace.push(order);
                if (DEBUG) {
                    window.debugShowAvialbePoint.push([order, [g_vertices[order[0]], g_vertices[order[1]], g_vertices[order[2]]]]);
                    window.needCheckedContourIndex.push([order, [g_vertices[order[0]], g_vertices[order[1]], g_vertices[order[2]]]]);
                }
            }
        }

        function contact(contour1, contour2) {
            for (var i = 0; i < contour1.length; i++) {
                var aIndex = contour1[i];
                var bIndex = contour1[(i + 1) % contour1.length];

                for (var j = 0; j < contour2.length; j++) {
                    var cIndex = contour2[j];

                    var order = GeometryUtils.orderVerticesByNormal(aIndex, bIndex, cIndex, g_vertices, faceNormal);

                    if (order == undefined) {
                        continue;
                    }

                    if (!checkVerticesAvailable(g_vertices[order[0]], g_vertices[order[1]], g_vertices[order[2]], order)) {
                        continue;
                    }

                    addFaceToArray(order);


                }
            }

        }

        var allHoleVerticesIndex = [];
        holeContourIndexArray.forEach(function (holeContourIndex) {
            holeContourIndex.forEach(function (index) {
                allHoleVerticesIndex.push(index);
            });
        });
        if (DEBUG) {
            window.allHoleVerticesIndex = allHoleVerticesIndex;
        }

        contact(faceVerticesIndexArray, allHoleVerticesIndex);

        holeContourIndexArray.forEach(function (holeContourIndex) {
            contact(holeContourIndex, faceVerticesIndexArray);
        });
        //孔位之间
        for (var i = 0; i < holeContourIndexArray.length - 1; i++) {
            var holeContour = holeContourIndexArray[i];
            var holeContour1 = holeContourIndexArray[(i + 1) % holeContourIndexArray.length];
            contact(holeContour, holeContour1);
            contact(holeContour1, holeContour);
        }

        for (var i = 0; i < holeContourIndexArray.length - 1; i++) {
            var T1 = holeContourIndexArray[i];
            var T2 = holeContourIndexArray[(i + 1) % holeContourIndexArray.length];

            for (var j = 0; j < T1.length; j++) {
                for (var k = 0; k < T2.length; k++) {
                    contact([T1[j], T2[k]], faceVerticesIndexArray);
                }
            }
        }

        if (DEBUG) {
            console.log("debugShowLineIntersect");
            console.log(window.debugShowLineIntersect);

            console.log("debugShowBeCover");
            console.log(window.debugShowBeCover);


            console.log("debugShowAvailablePoint");
            console.log(window.debugShowAvialbePoint);

            console.log("needCheckedContourIndex");
            console.log(window.needCheckedContourIndex);

        }
    },


    //把打孔的顶点加入到数组中
    addHolePointToVerticesInNotExist: function (extraVertices, g_vertices) {

        var indexArray = MathUtils.arrayContainsArray(g_vertices, extraVertices);
        if (indexArray) {
            return indexArray;
        } else {
            var count = g_vertices.length;
            var extraVerticesIndex = [];
            for (var i = 0; i < extraVertices.length; i++) {
                g_vertices.push(extraVertices[i]);
                extraVerticesIndex.push(count + i);
            }
            return extraVerticesIndex;
        }


    }
};

/**
 * Created by scenic on 2017/8/2.
 */
THREE.BufferGeometry.prototype.computeFaces = (function () {
    var geometry = new THREE.Geometry();

    return function (force) {
        if (force || this.faces == undefined || this.vertices == undefined) {
            geometry.vertices.deleteAll();
            geometry.faces.deleteAll();
            geometry.colors.deleteAll();
            geometry.faceVertexUvs[0].deleteAll();
            geometry.morphNormals.deleteAll();
            geometry.morphNormals.deleteAll();
            geometry.skinIndices.deleteAll();
            geometry.skinWeights.deleteAll();
            geometry.lineDistances.deleteAll();
            geometry.boundingBox = null;
            geometry.boundingSphere = null;

            geometry.fromBufferGeometry(this);
            geometry.mergeVertices();
            this.faces = geometry.faces;
            this.vertices = geometry.vertices;
            this.faceVertexUvs = geometry.faceVertexUvs;
        }
    }

})();
THREE.BufferGeometry.prototype.fromFaceArray = function (faces, vertices) {
    var position = [];
    var normals = [];
    var uvs = [];

    var size = new Box3().setFromPoints(vertices).getSize();
    var targetNormal = new Vector3(0, 0, 1);
    var quaternion = new Quaternion();
    var matrix = new Matrix4();
    var tmpNormal = new Vector3();
    var verticesA = new Vector3();
    var verticesB = new Vector3();
    var verticesC = new Vector3();
    var sizeTmp = new Vector3();

    function makeRotation(point, normal) {
        quaternion.setFromUnitVectors(normal, targetNormal);
        point.applyQuaternion(quaternion).round();
        return point;
    }

    faces.forEach(function (f) {
        var a = f.a;
        var b = f.b;
        var c = f.c;

        position.push(vertices[a].x);
        position.push(vertices[a].y);
        position.push(vertices[a].z);

        position.push(vertices[b].x);
        position.push(vertices[b].y);
        position.push(vertices[b].z);

        position.push(vertices[c].x);
        position.push(vertices[c].y);
        position.push(vertices[c].z);

        var normal = MathUtils.getNormal(vertices[a], vertices[b], vertices[c], tmpNormal);

        normals.push(normal.x);
        normals.push(normal.y);
        normals.push(normal.z);

        normals.push(normal.x);
        normals.push(normal.y);
        normals.push(normal.z);

        normals.push(normal.x);
        normals.push(normal.y);
        normals.push(normal.z);

        sizeTmp.copy(size).multiplyScalar(0.5);
        matrix.makeTranslation(sizeTmp.x, sizeTmp.y, sizeTmp.z);
        verticesA.copy(vertices[a]).applyMatrix4(matrix);
        verticesB.copy(vertices[b]).applyMatrix4(matrix);
        verticesC.copy(vertices[c]).applyMatrix4(matrix);

        makeRotation(verticesA, normal);
        makeRotation(verticesB, normal);
        makeRotation(verticesC, normal);
        makeRotation(sizeTmp.copy(size), normal).positive();

        uvs.push(verticesA.x / sizeTmp.x);
        uvs.push(verticesA.y / sizeTmp.y);

        uvs.push(verticesB.x / sizeTmp.x);
        uvs.push(verticesB.y / sizeTmp.y);

        uvs.push(verticesC.x / sizeTmp.x);
        uvs.push(verticesC.y / sizeTmp.y);
    });

    console.log("normal:");
    console.log(normals);
    console.log("\n\n");
    console.log("uvs");
    console.log(uvs);

    this.removeAttribute('position');
    this.addAttribute('position', new THREE.Float32BufferAttribute(new Float32Array(position), 3));

    this.removeAttribute('normal');
    this.addAttribute('normal', new THREE.Float32BufferAttribute(new Float32Array(normals), 3));

    this.removeAttribute('uv');
    this.addAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));

    return this;
};
THREE.BufferGeometry.prototype.makeHole = function (holeConfigArray) {

    /*
     思路：
     参数配置：
     [
     {faceNormal:a,holes: [ {holeVertices:[a,a],deep:a}   ] }
     ]
     */

    var DEBUG = this.debug;

    this.computeBoundingBox();
    this.computeFaces(true);
    var g_faces = this.faces;
    var g_vertices = this.vertices;

    function getHoleEndVertices(holeStartVertices, holeDirection, holeDeep) {
        var endVertices = [];
        var offSet = new Vector3();
        holeStartVertices.forEach(function (point) {
            var backPoint = point.clone();
            offSet.set(holeDeep, holeDeep, holeDeep);
            offSet.multiply(holeDirection);
            backPoint.add(offSet);
            endVertices.push(backPoint);
        });
        return endVertices;
    }

    function doMakeHole(faceNormal, holeStartContourArray) {

        var contourArrayStart = [];
        var contourArrayEnd = [];
        var contourEndIndexArray = [];
        var contourStartIndexArray = [];
        var backNormal = faceNormal.clone().negate();
        holeStartContourArray.forEach(function (holeEntity) {
            var holeContour = holeEntity.holeContour;
            var deep = holeEntity.deep;
            var direction = holeEntity.direction == undefined ? backNormal : holeEntity.direction;
            contourArrayStart.push(holeContour);
            var endContour = getHoleEndVertices(holeContour, direction, deep);
            contourArrayEnd.push(endContour);
            contourStartIndexArray.push(GeometryUtils.addHolePointToVerticesInNotExist(holeContour, g_vertices));
            contourEndIndexArray.push(GeometryUtils.addHolePointToVerticesInNotExist(endContour, g_vertices));
        });

        //镂空前面的面
        GeometryUtils.hollowFace(faceNormal, contourArrayStart, g_faces, g_vertices);

        //孔位前后相连接
        function connectStartEnd(startContour, endContour) {
            for (var i = 0; i < startContour.length; i++) {
                var a = startContour[i];
                var b = startContour[(i + 1 ) % startContour.length];
                var a1 = endContour[i];
                var b1 = endContour[(i + 1 ) % endContour.length];

                // g_faces.push(new Face3(a, a1, b, faceNormal));
                // g_faces.push(new Face3(b, a1, b1, faceNormal));

                g_faces.push(new Face3(a, b, a1, faceNormal));
                g_faces.push(new Face3(b, b1, a1, faceNormal));

            }
        }

        contourStartIndexArray.forEach(function (contour, i) {
            connectStartEnd(contour, contourEndIndexArray[i]);
        });

        //检测是否镂空背面
        var faces = GeometryUtils.pickFaceByNormal(backNormal, g_faces);
        var plane = new Plane().setFromNormalAndCoplanarPoint(backNormal, g_vertices[faces[0].a]);
        var contourInBackFaces = [];
        contourArrayEnd.forEach(function (contour) {
            var pointInBackFace = [];
            contour.forEach(function (p) {
                if (plane.distanceToPoint(p) == 0) {
                    pointInBackFace.push(p);
                }
            });
            if (pointInBackFace.length > 0) {
                contourInBackFaces.push(pointInBackFace);
            }

        });
        if (contourInBackFaces.length > 0) {
            GeometryUtils.hollowFace(backNormal, contourInBackFaces, g_faces, g_vertices);
        }


    }

    holeConfigArray.forEach(function (config) {
        //需要打孔的面
        var faceNormal = CommonUtils$1.notNull(config.faceNormal);
        //当前面上需要打的孔位信息
        var holes = config.holes;

        if (DEBUG) {
            console.log("config is ");
            console.log(config);
        }

        doMakeHole(faceNormal, holes);

    });
};

/**
 * Created by scenic on 2017/8/2.
 */

HTMLElement.prototype.getCacheBoundingClientRect = function () {
    if (this.boundingClientRect == undefined) {
        console.log("bounding rect resize:");
        console.log(this);
        console.log("\n");
        this.boundingClientRect = this.getBoundingClientRect();
    }
    return this.getBoundingClientRect() //this.boundingClientRect;
};

HTMLElement.prototype.checkAvailability = function () {
    if (this.parentNode == null) {
        throw new Error("the element must be added the container")
    }
};

/**
 * Created by scenic on 2017/8/2.
 */
var OnSceneChangeListener = new ListenerExecute();

function Object3D() {
    THREE.Object3D.call(this);
}


Object3D.prototype = Object.assign(Object.create(THREE.Object3D.prototype), {
    constructor: Object3D,
    add: function (object) {
        THREE.Object3D.prototype.add.call(this, object);
        if (object == undefined) {
            throw new Error("the object is null")
        }
        var parent = this;
        while (parent != null && !(parent instanceof THREE.Scene)) {
            parent = parent.parent;
        }

        if (parent instanceof THREE.Scene && !(object instanceof THREE.Scene)) {
            //add the edges auto
            object.traverse(function (mesh) {
                if (mesh.edgesHelper) {
                    parent.add(mesh.edgesHelper);
                }
            });
        }

        OnSceneChangeListener.forEach(function (l) {
            l.call(l, "add", object);
        });
    },

    remove: function (object) {
        THREE.Object3D.prototype.remove.call(this, object);

        if (object == undefined) {
            return;
        }

        if ('destroy' in object) {
            object.destroy();
        } else {
            console.error("can not destroy ");
            console.error(object);
        }


        var parent = this;
        while (!(parent instanceof THREE.Scene)) {
            parent = parent.parent;
        }
        if (parent instanceof THREE.Scene) {
            object.traverse(function (mesh) {
                if (mesh.edgesHelper) {
                    parent.remove(mesh.edgesHelper);
                }
            });
        }
        OnSceneChangeListener.forEach(function (l) {
            l.call(l, "remove", object);
        });
    }

});

/**
 * Created by scenic on 2017/8/2.
 */
THREE.Mesh.prototype.setAxisByVector = (function () {

    var meshGeometryClone, helperGeometryClone;
    var box3 = new THREE.Box3();
    var offset = new THREE.Vector3();
    // var  points = new THREE.Points(geometry,new THREE.PointsMaterial({color:"#FF0000",size:30}));
    // var flag = new THREE.Mesh(new THREE.BoxGeometry(10,1000,10))
    // var flag1 = new THREE.Mesh(new THREE.BoxGeometry(5,500,5))

    return function (vector) {

        var oldCenterPosition = box3.setFromObject(this).getCenter();

        var geometry = this.geometry;
        //还原最开始的geometry的状态
        if (meshGeometryClone == undefined) {
            meshGeometryClone = geometry.clone();
        }
        geometry.copy(meshGeometryClone);
        geometry.translate(vector.x, vector.y, vector.z);

        //还原边缘帮助的geometry
        if (this.edgesHelper) {
            if (helperGeometryClone == undefined) {
                helperGeometryClone = this.edgesHelper.geometry.clone();
            }

            this.edgesHelper.geometry.copy(helperGeometryClone);
            this.edgesHelper.geometry.translate(vector.x, vector.y, vector.z);
        }

        //计算mesh的偏移量
        geometry.computeBoundingBox();
        var geometryCenter = geometry.boundingBox.getCenter().applyMatrix4(this.matrixWorld);
        offset.copy(oldCenterPosition).sub(geometryCenter);
        this.position.add(offset);

        //debug
        // var scene = this.parent;
        // scene.add(points)
        // scene.add(flag);
        // scene.add(flag1);
        // flag1.position.copy(geometryCenter);
        // flag.position.copy(oldCenterPosition);
    }
})();
THREE.Mesh.prototype.setAxis = function (axis) {
    this.geometry.computeBoundingBox();
    var box3 = this.geometry.boundingBox;
    var size = box3.getSize();
    if (axis.contains('lbf')) {
        //左下角，前方

        this.setAxisByVector(new THREE.Vector3(size.x / 2, size.y / 2, -size.z / 2));
        // this.setAxisByVector(new THREE.Vector3(50, 0, 50));
    } else if (axis.contains('rbf')) {
        //右下角，前方
        this.setAxisByVector(new THREE.Vector3(-size.x / 2, size.y / 2, -size.z / 2));
    } else if (axis.contains("center")) {
        var center = box3.getCenter();
        this.setAxisByVector(new THREE.Vector3().sub(center));
    } else {
        throw new Error("can not match the option " + axis);
    }
};
THREE.Mesh.prototype.holdEdgesScale = (function () {
    var dimension = new Vector3();
    var offSetScale = new Vector3();
    var quaternion = new Quaternion();
    var offsetPosition = new Vector3();
    return function (direction, offset) {
        if(offset != 0){
            var tmpVale = offset / Math.abs(offset);// -1 或者 1
            offSetScale.copy(direction).multiplyScalar(offset).applyQuaternion(this.getWorldQuaternion(quaternion)).positive().multiplyScalar(tmpVale);
            this.scaleToDimension(this.getDimension(dimension).add(offSetScale));
            offsetPosition.copy(direction).multiplyScalar(0.5 * offset);
            this.position.add(offsetPosition);
        }

    }
})();
THREE.Mesh.prototype.setVisible = function (visible) {
    this.visible = visible;
    if (this.edgesHelper) {
        this.edgesHelper.visible = visible;
    }
};

THREE.Mesh.prototype.add = function (object) {
    Object3D.prototype.add.call(this, object);
};

THREE.Mesh.prototype.remove = function (object) {
    Object3D.prototype.remove.call(this, object);
};

THREE.Group.prototype.add = function (object) {
    Object3D.prototype.add.call(this, object);
};

THREE.Group.prototype.remove = function (object) {
    Object3D.prototype.remove.call(this, object);
};

THREE.Scene.prototype.add = function (object) {
    Object3D.prototype.add.call(this, object);
};

THREE.Scene.prototype.remove = function (object) {
    Object3D.prototype.remove.call(this, object);
};

/**
 * Created by scenic on 2017/8/2.
 */
THREE.Object3D.prototype.setName = function (name) {
    this.name = name;
    return this;
};
THREE.Object3D.prototype.getDimension = function (optionalTarget) {
    var result = optionalTarget || new Vector3();
    this.geometry.computeBoundingBox();
    var box3 = this.geometry.boundingBox;
    box3.getSize(result).multiply(this.scale);
    return result;
};

THREE.Object3D.prototype.scaleToDimension = (function () {
    var tmpSize = new Vector3();
    return function (vector) {
        var warning = false;
        if (!vector.isVector3) {
            console.error("the param need vector3");
        }

        vector.errorNaNOrInfinity();

        if (warning) {
            if (vector.x <= 0) {
                console.warn("the scale x is less 0 ");
                console.log(vector);
            }
            if (vector.y <= 0) {
                console.warn("the scale y is less 0");
                console.log(vector);
            }
            if (vector.z <= 0) {
                console.warn("the scale z is less 0");
                console.log(vector);
            }
        }

        this.geometry.computeBoundingBox();
        var size = this.geometry.boundingBox.getSize(tmpSize).errorNaNOrInfinity();
        this.scale.set(vector.x / size.x, vector.y / size.y, vector.z / size.z);
        return this;
    }
})();
THREE.Object3D.prototype.destroy = function () {
    function destroyExecute(object) {
        var geometry = object.geometry;
        var material = object.material;
        if (geometry) {
            geometry.dispose();
        }

        if (material) {
            if (material instanceof THREE.MultiMaterial || material instanceof Array) {
                var materialArray = material instanceof Array ? material : material.materials;
                materialArray.forEach(function (m) {
                    m.dispose();
                    var texture = m.map;
                    if (texture) {
                        texture.dispose();
                    }
                });
            } else {
                material.dispose();
                var texture = material.map;
                if (texture) {
                    texture.dispose();
                }
            }
        }
    }

    this.traverse(function (object) {
        if (object.edgesHelper) {
            destroyExecute(object.edgesHelper);
        }
        destroyExecute(object);
    });

    this.destroyed = true;
};
THREE.Object3D.prototype.dispose = function () {
    this.destroy();
};
THREE.Object3D.prototype.setVisible = function (visible) {
    this.visible = visible;
    if (this.edgesHelper) {
        this.edgesHelper.visible = visible;
    }
};
THREE.Object3D.prototype.synchronizeTranslate = (function () {
    var quaternion = new THREE.Quaternion();

    return function (object) {
        object.updateMatrixWorld(true);
        var matrixWorld = object.matrixWorld;
        this.position.setFromMatrixPosition(matrixWorld);
        this.scale.setFromMatrixScale(matrixWorld);
        // helper.rotation.setFromRotationMatrix(tempMatrix.extractRotation(matrixWorld));
        this.quaternion.set(0, 0, 0, 1).multiply(object.getWorldQuaternion(quaternion.set(0, 0, 0, 1)));
    }

})();

THREE.Vector3.prototype.positive = function () {
    if (this.x < 0) {
        this.x = -this.x;
    }

    if (this.y < 0) {
        this.y = -this.y;
    }
    if (this.z < 0) {
        this.z = -this.z;
    }

    return this;
};
THREE.Vector3.prototype.isContainNegative = function () {
    if (this.x < 0) {
        return true;
    }

    if (this.y < 0) {
        return true;
    }
    if (this.z < 0) {
        return true;
    }

    return false;
};
THREE.Vector3.prototype.errorNegativeValue = function () {
    if (this.isContainNegative()) {
        throw new Error("this vector contain negative component");
    }
    return this;
};
THREE.Vector3.prototype.errorNaNOrInfinity = function () {

    var x = this.x;
    var y = this.y;
    var z = this.z;

    [x,y,z] .forEach(function (vale) {
        if(vale == Infinity || isNaN(vale)){
            throw new Error("the value contain inFinite or NaN " + x  + " " + y + " " + z);
        }
    });
    return this;
};
THREE.Vector3.prototype.equalsFloat = function (v) {
    if (!CommonUtils$1.notNull(v).isVector3) {
        console.error(v);
        throw new Error("the param must be Vector3")
    }

    function check(a, b) {
        return MathUtils.floatEquals(a, b);
    }

    return check(this.x, v.x) && check(this.y, v.y) && check(this.z, v.z);
};



THREE.Raycaster.prototype.intersectScene = (function () {
    var array = [];
    return function intersectScene(sceneObjects) {
        array.deleteAll();
        for (var i = 0; i < sceneObjects.length; i++) {
            var object = sceneObjects[i];
            if (object.raycastSensitivity != false) {
                array.push(object);
            }
        }
        return this.intersectObjects(array, true);
    }
})();
THREE.Scene.prototype.contains = function (object) {
    return this.children.indexOf(object) != -1;
};

THREE.Line3.prototype.intersectLine = (function () {
    var pa = new Vector3();
    var pb = new Vector3();

    return function (line3, strict) {
        var DEBUG = this.debug;
        if (DEBUG) {
            DebugDraw.drawArrow(this, "#FF0000");
            DebugDraw.drawArrow(line3, "#00FF00");
        }
        var intersectPoint = MathUtils.getLineIntersectPoint(this, line3);
        if (intersectPoint == null) {
            return false;
        }

        var p = intersectPoint;

        if ((strict == true) &&
            (p.equalsFloat(line3.start) || p.equalsFloat(line3.end) ||
            p.equalsFloat(this.start) || p.equalsFloat(this.end))) {
            //如果是严格的相交的模式的话，必须是十字相交，而丁字相交这里不作数
            return false;
        }


        return MathUtils.isPointInLine(this, p) && MathUtils.isPointInLine(line3, p);

        // var a = line3.start;
        // var b = line3.end;
        // pa.subVectors(a,p).normalize();
        // pb.subVectors(b,p).normalize();
        // var dot = pa.dot(pb);
        // if(DEBUG){
        //     console.log("degrees is " + MathUtils.getDegree(dot) + " dot " + dot );
        // }
        // if(dot > 0){
        //     return false;
        // }else {
        //     return true;
        // }
    }
})();

/**
 * Created by scenic on 2017/8/2.
 */

String.prototype.format = function () {
    var ss = this.split(/\{\d+?\}/);
    for (var i = 0; i < ss.length; i++) {
        if (arguments[i] == undefined) {
            break;
        }
        ss[i] += arguments[i];
    }

    return ss.join("");
};
String.prototype.contains = function (str) {
    return this.indexOf(str) != -1;
};

/**
 * Created by scenic on 2017/8/2.
 */

function Scene(name, defaultViewport, config) {
    THREE.Scene.call(this);
    config = config || {};

    this.name = name;
    this.viewport = defaultViewport;
    this.translate = defaultViewport.translate;
    this.sceneColor = undefined;
    this.gridHelper = undefined;
    this.axisHelper = undefined;
    this.arrowHelper = undefined;
    // this.updateGridHelper();
    // this.updateAxisHelper();
    // this.updateArrowHelper();
    this.add(this.translate);

    if (this.viewport != undefined) {
        this.viewport.associateScene = this;
    }
}

Scene.prototype = Object.assign(Object.create(THREE.Scene.prototype), {
    constructor: Scene,
    attachTranslate: function (object) {
        this.translate.attach(object);
    },
    detachTranslate: function () {
        this.translate.detach();
    },
    updateAxisHelper: function () {
        if (this.axisHelper == undefined) {
            this.axisHelper = new THREE.AxisHelper(2000);
            this.axisHelper.raycastSensitivity = false;
        }
        if (this.contains(this.axisHelper)) {
            this.remove(this.axisHelper);
        } else {
            this.add(this.axisHelper);
        }
    },
    updateArrowHelper: function () {
        if (this.arrowHelper == undefined) {
            this.arrowHelper = new THREE.ArrowHelper(new THREE.Vector3(0, 0, 1), new THREE.Vector3());
        }
        if (this.contains(this.arrowHelper)) {
            this.remove(this.arrowHelper);
        } else {
            this.add(this.arrowHelper);
        }
    },

    updateGridHelper: function () {
        if (this.gridHelper == undefined) {
            var size = 2000, divisions = 40;
            this.gridHelper = new THREE.GridHelper(size, divisions);
            this.gridHelper.size = size;
            this.gridHelper.divisions = divisions;
            this.gridHelper.raycastSensitivity = false;
        }

        if (this.contains(this.gridHelper)) {
            this.remove(this.gridHelper);
        } else {
            this.add(this.gridHelper);
        }
    },

    isTranslateVisible: function () {
        return this.translate != undefined && this.translate.axis != null;
    },

    /**
     * if the scene want to be rendered, it must have a viewport object
     * @param viewport
     */
    notifyElementChange: function (viewport) {
        if (this.translate !== viewport.translate) {

            //copy the last viewport`s translate property to the current
            if (this.translate.object) {
                //attach object
                viewport.translate.attach(this.translate.object);
            }
            viewport.translate.setSpace(this.translate.space);
            viewport.translate.setTranslationSnap(this.translate.translationSnap);
            viewport.translate.setMode(this.translate.model);


            if (this.translate.viewport == viewport) {
                //the duplicate translate will be remove if the one have the same viewport
                this.remove(this.translate);
                this.translate.dispose();
            } else {
                //hide the last viewport`s translate
                this.translate.visible = false;
            }


            //show the current viewport`s translate
            if (!this.contains(viewport.translate)) {
                this.add(viewport.translate);
            } else {
                viewport.translate.visible = true;
            }

            this.translate = viewport.translate;
            console.log(this.translate);
            console.log("scene(" + this.name + ") change the viewport (" + viewport.name + ")");
        }
    },

});

/**
 * Created by scenic on 2017/8/2.
 */

/**
 *表示一个现实窗口，有区域，摄像机，控制器等构成
 * 一个viewport 严格的对应一个element的区域
 * @param element
 * @param camera
 * @constructor
 */


function Viewport$1(element, scene) {
    this.id = Viewport$1.prototype.ID++;

    //create default camera
    this.perspective = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100000);
    this.perspective.position.set(0, 1000, 2000);
    this.orthographic = new THREE.OrthographicCamera(-window.innerWidth / 2, window.innerWidth / 2, window.innerHeight / 2, -window.innerHeight / 2, 1, 50000);
    this.orthographic.position.set(0, 1000, 0);


    this.domElement = element;
    //this field change base on the camera
    this.control = undefined;
    this.translate = undefined;
    this.camera = this.changeCamera("p");
    this.clearColor = "#452567";

    this._associateScene = scene;

    element.wrapper = this;
    Object.defineProperty(this, "associateScene", {
        get: function () {
            return this._associateScene;
        },
        set: function (associateScene) {
            console.log("try set the associateScene " + associateScene + " Viewport: " + this.id);
            this._associateScene = associateScene;
            if (this.camera.name == '') {
                this.camera.name = "camera:" + associateScene.name;
            }
            if (this.control.name == undefined) {
                this.control.name = "control:" + associateScene.name;
            }
        }
    });

}

Viewport$1.prototype = Object.assign({
    ID: 0,
    changeControl: function (camera) {
        function changeControl() {
            if (this.control) {
                this.control.dispose();
            }
            this.control = new THREE.OrbitControls(camera, this.domElement);
            // this.control = new THREE.OrthographicTrackballControls(camera,this.domElement);
            // this.control = new THREE.TrackballControls(camera,this.domElement);
            var control = this.control;
            control.id = "OrbitControls";
            control.name = "viewportID: " + this.id;
            control.addEventListener('change', function () {
                control.state = 'change';
            });
            control.addEventListener('start', function () {
                control.state = 'start';
            });
            control.addEventListener('end', function () {
                control.state = 'end';
            });
            control.update();
        }


        function changeTranslate() {
            if (camera.translate != this.translate) {
                if (this.translate) {
                    this.translate.dispose();
                }
                this.translate = Config.defaultGetTransformControls(camera, this.domElement);
                this.translate.viewport = this;
                this.translate.camera = camera;
                this.translate.domElement = this.domElement;
                this.translate.name = "viewPort " + this.id;
                camera.translate = this.translate;

                if (camera instanceof THREE.OrthographicCamera) {
                    this.translate.setSize(0.1);
                }

            } else {
                this.translate = camera.translate;
            }
            this.translate.update();

        }

        changeControl.call(this);
        changeTranslate.call(this);

    },
    changeCamera: function (type) {
        if (type == 'p') {
            this.camera = this.perspective;
        } else if (type == 'o') {
            this.camera = this.orthographic;
        } else if (type == undefined) {
            if (this.camera == this.perspective) {
                this.changeCamera("o");
            } else {
                this.changeCamera("p");
            }
        }

        this.changeControl(this.camera);
        return this.camera;
    }
});

/**
 * Created by scenic on 2017/8/2.
 */


/**
 * it will return true if the array container the element
 * it will return true if the element have been defined in the document
 * @param element
 * @param array
 * @returns {boolean}
 */
function isBodyContainerElement(element, array) {
    if (document.getElementById(element.id) != undefined) {
        return true;
    }
    if (array == undefined) {
        array = window.document.body.children;
    }

    for (var i = 0; i < array.length; i++) {
        if (array[i] === element) {
            return true;
        }
    }
    return false;
}

function defaultConfig(flag, param1, param2) {
    if (flag == 'container') {
        var container = document.createElement("div");
        var style = container.style;
        style.position = "relative";
        style.height = window.innerHeight + "px";
        // container.style.position = "fixed"
        //style.backgroundColor = "#FF0000"
        // style.position = 'absolute';
        // style.marginRight = '0px';
        // style.left = "20%";
        // style.width = "79%"
        // style.height = "90%";
        // style.top = '100px';
        // style.zIndex = 1;
        return container;
    }
    else if (flag == 'canvas') {
        var canvas = document.createElement("canvas");
        canvas.style.position = "absolute";
        canvas.style.top = "0px";
        canvas.style.left = "0px";
        canvas.style.width = "100%";
        canvas.style.height = "100%";
        return canvas;
    }
    else if (flag == 'renderer') {
        var renderParam = {
            alpha: true,
            antialias: true,
            canvas: param1,
            // stencil:false,
            // precision: "highp",
            precision: 'highp',
            // alpha: true,
            preserveDrawingBuffer: true,//是否保存绘图缓冲
            //     maxLights:1           //maxLights:最大灯光数
            // logarithmicDepthBuffer: false,
        };
        var renderer = new THREE.WebGLRenderer(renderParam);
        // renderer.setClearColor(0xff0000)
        renderer.setPixelRatio(1);
        // renderer.setPixelRatio(window.devicePixelRatio);
        renderer.sortObjects = false;
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.BasicShadowMap;
        // renderer.shadowMap.type = THREE.PCFShadowMap;
        renderer.gammaInput = true;
        renderer.gammaOutput = true;
        renderer.physicallyCorrectLights = true;

        // renderer = new THREE.CanvasRenderer()
        return renderer;
    }

    else if (flag == 'control') {


        return control;
    }
    else if (flag == 'translate') {
        var camera = param1;
        var domElement = param2;
        return Config.defaultGetTransformControls(camera, domElement);
    }

}

function Start(parameters) {

    this.OnRePaintListener = new ListenerExecute();
    this.OnMouseMoveListener = new ListenerExecute();
    this.OnMouseDownListener = new ListenerExecute();
    this.OnMouseUpListener = new ListenerExecute();
    this.OnMouseWheelListener = new ListenerExecute();
    this.OnDoubleClickListener = new ListenerExecute();

    this.renderCleanColor = "#d9d9d9";
    var scope = this;

    parameters = parameters || {};

    this.autoCheckContainerSize = true;

    this.scenes = [];
    this.viewports = [];
    this.container = parameters.container != undefined ? parameters.container : defaultConfig("container");
    this.canvas = parameters.canvas != undefined ? parameters.canvas : defaultConfig("canvas");

    this.canvas2d = defaultConfig("canvas");

    this.canvas.name = "default_canvas_3D";
    this.canvas2d.name = "default_canvas_2D";

    this.context = this.canvas2d.getContext('2d');

    this.renderer = parameters.renderer != undefined ? parameters.renderer : defaultConfig("renderer", this.canvas);

    //add the container to body
    if (!isBodyContainerElement(this.container)) {
        window.document.body.appendChild(this.container);
    } else {
        console.log("document body already add the container ");
    }

    //add the canvas to container
    if (!isBodyContainerElement(this.canvas, this.container.children)) {
        this.container.appendChild(this.canvas);
    } else {
        console.log("container already add the canvas");
    }

    if (!isBodyContainerElement(this.canvas2d, this.container.children)) {
        this.container.appendChild(this.canvas2d);
    } else {
        console.log("container already add the canvas");
    }

    //register event
    window.addEventListener('resize', function (event) {
        scope.clearBoundingClientCache();

        if (!scope.autoCheckContainerSize) {
            scope.checkAndUpdateCanvasSize();
        }

    }, false);

    window.addEventListener('touchmove', function (event) {

    }, false);

    if (!this.autoCheckContainerSize) {
        this.checkAndUpdateCanvasSize();
    }

}

Start.prototype = {
    constructor: Start,
    clearCanvas: function () {
        this.renderer.setClearColor(this.renderCleanColor);
        this.renderer.setScissorTest(false);
        this.renderer.clear();
    },
    clearBoundingClientCache: function () {
        var childNodes = this.container.childNodes;
        childNodes.forEach(function (element) {
            element.boundingClientRect = undefined;
        });
    },
    render: function (scene, camera) {

        this.renderer.render(scene, camera);
    },
    renderMultipleScene: function () {
        var scope = this;
        if (this.autoCheckContainerSize) {
            this.checkAndUpdateCanvasSize();
        }

        for (var i = 0; i < this.scenes.length; i++) {
            var scene = this.scenes[i];
            var viewport = scene.viewport;
            var camera = viewport.camera;

            this.setViewPortByElement(viewport);

            this.renderer.setClearColor(scene.sceneColor);

            for(var j = 0;j < this.OnRePaintListener.listenerArray.length; j++){
                this.OnRePaintListener.listenerArray[j].call(scope,scene,camera,"renderScene");
            }
            this.render(scene, camera);

        }

    },
    renderMultipleView: function () {
        var scope = this;
        if (this.autoCheckContainerSize) {
            this.checkAndUpdateCanvasSize();
        }
        for (var i = 0; i < this.viewports.length; i++) {
            var viewport = this.viewports[i];
            var camera = viewport.camera;
            var scene = viewport.associateScene;
            this.setViewPortByElement(viewport);

            this.renderer.setClearColor(viewport.clearColor);
            for(var j = 0;j < this.OnRePaintListener.listenerArray.length; j++){
                this.OnRePaintListener.listenerArray[j].call(scope,scene,camera,"renderView");
            }
            this.render(scene, camera);
        }
    },
    checkAndUpdateCanvasSize: function () {
        var widthContainer = this.container.clientWidth;
        var heightContainer = this.container.clientHeight;

        var _canvas = this.canvas;
        var widthCanvas = _canvas.width;
        var heightCanvas = _canvas.height;

        if (_canvas.width != widthContainer || _canvas.height != heightContainer) {
            console.log("widthContainer " + widthContainer + "  heightContainer  " + heightContainer + "  widthCanvas " + widthCanvas + "  heightCanvas  " + heightCanvas);
            this.renderer.setSize(widthContainer, heightContainer, false);
            this.canvas2d.width = _canvas.width;
            this.canvas2d.height = _canvas.height;
            console.log("change the render size " + widthContainer + "  " + heightContainer);
        }
    },
    setViewPortByElement: function (viewport) {
        var element = viewport.domElement;
        element.checkAvailability();

        var rectCanvas = this.canvas.getCacheBoundingClientRect();
        var rectElement = element.getCacheBoundingClientRect();

        //view port rect
        var x = rectElement.left - rectCanvas.left;
        var y = rectCanvas.height - rectElement.height - ( rectElement.top - rectCanvas.top);
        // var y = rectElement.top - rectCanvas.top;

        var width = rectElement.width;
        var height = rectElement.height;
        this.renderer.setScissorTest(true);
        this.renderer.setViewport(x, y, width, height);
        this.renderer.setScissor(x, y, width, height);
        viewport.width = width;
        viewport.height = height;


        // 更新摄像机
        var camera = viewport.camera;
        var aspect = width / height;
        if (camera.isOrthographicCamera) {
            if (
                camera.left != -0.5 * width ||
                camera.right != 0.5 * width ||
                camera.top != 0.5 * height ||
                camera.bottom != -0.5 * height
            ) {
                camera.left = -0.5 * width;
                camera.right = 0.5 * width;
                camera.top = 0.5 * height;
                camera.bottom = -0.5 * height;
                camera.updateProjectionMatrix();
                console.log("update project matrix");
            }

        } else if (camera.isPerspectiveCamera) {

            if (aspect != camera.aspect) {
                camera.aspect = aspect;
                camera.updateProjectionMatrix();
                console.log("update project matrix");
            }
        }

    },

    draw2DText: function (element, text, x, y, maxWidth) {
        element.checkAvailability();
        var rectCanvas = this.canvas2d.getCacheBoundingClientRect();
        var rectElement = element.getCacheBoundingClientRect();
        var startX = rectElement.left - rectCanvas.left;
        var startY = rectElement.top - rectCanvas.top;
        var offset = 0;
        //  this.context.clearRect(startX - offset, startY - offset , rectElement.width + offset, rectElement.height+ offset); // Clea
        this.context.fillStyle = "rgb(255,0,0)";
        this.context.fillText(text, startX + x, startY + y, maxWidth);
    },
    draw2DClear: function (element) {
        element.checkAvailability();
        var rectCanvas = this.canvas2d.getCacheBoundingClientRect();
        var rectElement = element.getCacheBoundingClientRect();
        var startX = rectElement.left - rectCanvas.left;
        var startY = rectElement.top - rectCanvas.top;
        var offset = 0;
        this.context.clearRect(startX - offset, startY - offset, rectElement.width + offset, rectElement.height + offset); // Clea
    },

    screenCapture: function () {
        this.render();
        return this.canvas.toDataURL("image/png");
    },
    addElementEvent: function (element) {
        var scope = this;
        element.addEventListener('mousemove', function (event) {
            scope.OnMouseMoveListener.forEach(function (l) {
                l.call(scope, event);
            });
        }, false);
        element.addEventListener('mousedown', function (event) {
            scope.OnMouseDownListener.forEach(function (l) {
                l.call(scope, event);
            });
        }, false);
        element.addEventListener('mouseup', function (event) {
            scope.OnMouseUpListener.forEach(function (l) {
                l.call(scope, event);
            });
        }, false);
        element.addEventListener("dblclick", function (event) {
            scope.OnDoubleClickListener.forEach(function (l) {
                l.call(scope, event);
            });
        }, false);
        element.addEventListener("wheel", function (event) {
            scope.OnMouseWheelListener.forEach(function (l) {
                l.call(scope, event);
            });
        });
    },
    newElement: function (rect, parent) {
        var DEBUG = this.debug;

        var element = document.createElement("div");
        element.style.position = "absolute";
        element.style.display = "inline-block";
        element.style.border = "1px solid #000";
        element.id = "newElement" + Date.now();
        var rectCanvas = this.canvas.getBoundingClientRect();
        var rectContainer = this.container.getBoundingClientRect();
        if (DEBUG) {
            console.log("rectCanvas : ");
            console.log(rectCanvas);
            console.log("rectContainer : ");
            console.log(rectContainer);
        }
        // element.style.backgroundColor = "#fff71f";
        var left = "x" in rect ? rect.x : rect.left;
        var top = "y" in rect ? rect.y : rect.top;
        var width = rect.width;
        var height = rect.height;
        var x = left + (rectCanvas.left - rectContainer.left);
        var y = top + (rectCanvas.top - rectContainer.top);

        element.style.left = x + "px";
        element.style.top = y + "px";
        element.style.width = (typeof width == 'string' && width.indexOf("%") != -1) ? width : width + "px";
        element.style.height = (typeof height == 'string' && height.indexOf("%") != -1) ? height : height + "px";
        this.addElementEvent(element);
        if (parent) {
            parent.appendChild(element);
        }
        return element;
    },
    newScene: function (name, rect, config) {
        config = config || {};

        // if the container have two canvas , the 2D canvas should be on top of the 3D canvas
        // so the 3D canvas will lost the mouse event. we will add new element on the top of all to resolve the problem
        // var element = rect != undefined ? this.newElement(rect, this.container) : this.canvas;

        var element = this.newElement(rect != undefined ? rect : {
            x: 0,
            y: 0,
            width: "100%",
            height: "100%"
        }, this.container);

        var scene = new Scene(name, new Viewport$1(element), config);
        this.scenes.push(scene);
        return scene;

    },
    dispose: function () {
        while (this.container.hasChildNodes()) //当div下还存在子节点时 循环继续
        {
            this.container.removeChild(this.container.firstChild);
        }
        //document.body.removeChild(this.container);
    },
    autoRender:function () {
        var scope = this;
        function animator() {
            requestAnimationFrame(animator);
            scope.renderMultipleScene();
        }
        animator();
    }
};

/**
 * Created by scenic on 2017/8/2.
 */
function StartWidthRayCast(parameters) {
    Start.call(this, parameters);

    var isObrControlWorking = false;
    this.rayCast = new THREE.Raycaster();
    var scope = this;
    this.mousePosition = new THREE.Vector2(0, 0);

    this.OnClickListener = new ListenerExecute();
    this.OnMouseMoveListener.addListener(function (event) {
        //fist we must make sure which element associate the TranslateControl object in scene
        var view = Utils.getCurrentViewport(event);
        var scene = view.associateScene;
        var domElement = view.domElement;
        scene.notifyElementChange(view);


        var domBounding = domElement.getBoundingClientRect();
        var viewPortWidth = domBounding.width;
        var viewPortHeight = domBounding.height;

        scope.mousePosition.set(
            ((event.clientX - domBounding.left ) / viewPortWidth) * 2 - 1,
            -((event.clientY - domBounding.top) / viewPortHeight) * 2 + 1
        );

    }, "forRayCast");

    this.OnMouseUpListener.addListener(function (event) {
        var viewport = Utils.getCurrentViewport(event);
        var scene = viewport.associateScene;
        var control = viewport.control;
        if (scene && !scene.isTranslateVisible() && control.state != 'change') {
            var intersects = scope.getRayCastIntersects(event);
            this.OnClickListener.forEach(function (l) {
                l.call(scope, event, intersects);
            });
        }
    }, "onMouseUp");

    this.OnMouseDownListener.addListener(function () {
        isObrControlWorking = false;
    }, "onMouseDown");
}

StartWidthRayCast.prototype = Object.assign(Object.create(Start.prototype), {
    constructor: StartWidthRayCast,
    getRayCastIntersects: function (event, camera, objectArray) {
        if (camera == undefined) {
            camera = Utils.getCurrentViewport(event).camera;
        }
        if (objectArray == undefined) {
            objectArray = Utils.getCurrentViewport(event).associateScene.children;
        }
        this.rayCast.setFromCamera(this.mousePosition, camera);
        return this.rayCast.intersectScene(objectArray);
    }
});

/**
 * Created by scenic on 2017/8/2.
 */

function CameraHelper(start) {
    var scene = start.newScene("camera", {x: 0, y: 0, width: 800, height: 500});
    scene.viewport.camera.far = 1000000;
    scene.viewport.camera.position.set(0, 0, 6000);
    scene.viewport.control.update();
    var element = scene.viewport.domElement;

    scene.sceneColor = "#4e5255";

    this.destroy = function () {
        start.OnRePaintListener.removeListener("camera-helper-showInfo");
        element.parentNode.removeChild(element);
    };
    this.showCameraHelper = function (monitorScene) {
        var camera = monitorScene.viewport.camera;
        var cameraHelper = new THREE.CameraHelper(camera);
        scene.add(cameraHelper);
        scene.add(monitorScene);
        (function () {
            function getNumber(pxValue) {
                if (pxValue) {
                    return parseInt(pxValue.slice(0, pxValue.length - 2));
                }
            }

            var startX, startY, currentLeft, currentTop;
            start.OnMouseMoveListener.addListener(function (event) {
                var showElement = scene.viewport.domElement;
                if (event.currentTarget == showElement) {
                    if (event.shiftKey) {
                        if (startX == undefined) {
                            startX = event.clientX;
                        }
                        if (startY == undefined) {
                            startY = event.clientY;
                        }
                        var offsetX = event.clientX - startX;
                        var offsetY = event.clientY - startY;
                        showElement.style.left = currentLeft + offsetX + "px";
                        showElement.style.top = currentTop + offsetY + "px";
                    } else {
                        currentLeft = getNumber(showElement.style.left);
                        currentTop = getNumber(showElement.style.top);

                        startX = startY = undefined;
                    }
                    start.clearBoundingClientCache();
                }
            });
        })();

        start.OnRePaintListener.addListener(function () {
            cameraHelper.update();

            var cameraDistance = parseFloat(camera.position.distanceTo(new THREE.Vector3())).toFixed(2);
            var fov = camera.fov;
            var textDistanceY = 30;

            var index = 1;

            function text(text) {
                start.draw2DText(element, text, 20, textDistanceY * index);
                index++;
            }

            start.draw2DClear(element);
            text("distance: " + cameraDistance);
            text("position: " +
                parseFloat(camera.position.x).toFixed(2) + " , " +
                parseFloat(camera.position.y).toFixed(2) + " , " +
                parseFloat(camera.position.z).toFixed(2));

            text("rotation: " +
                parseFloat(camera.rotation.x * (180 / Math.PI)).toFixed(2) + " , " +
                parseFloat(camera.rotation.y * (180 / Math.PI)).toFixed(2) + " , " +
                parseFloat(camera.rotation.z * (180 / Math.PI)).toFixed(2)
            );

            text("zoom " + camera.zoom);

            if (camera instanceof THREE.PerspectiveCamera) {
                text("fov: " + fov);
            } else if (camera instanceof THREE.OrthographicCamera) {
                text("left: " + camera.left);
                text("right: " + camera.right);
                text("top: " + camera.top);
                text("bottom: " + camera.bottom);
            }

        }, "camera-helper-showInfo");

        return this;
    };
}

/**
 * Created by scenic on 2017/8/2.
 */
function LightDebugHelper(scene, renderer, camera, lightConfig) {
    var callback = function (switchFlag) {
        return function (checkValue) {
            if (checkValue == true) {
                switch (switchFlag) {

                    case 'AmbientLight':
                        lightConfig.addAmbientLight();
                        showLightParamDebug(lightConfig.ambientLight);
                        break;
                    case 'DirectionLight':
                        lightConfig.addDirectionLight();
                        lightConfig.addDirectionLightHelper();
                        showLightParamDebug(lightConfig.directionalLight);

                        break;
                    case 'PointLight':
                        lightConfig.addPointLight();
                        lightConfig.addPointLightHelper();
                        showLightParamDebug(lightConfig.pointLight);

                        break;
                    case 'SpotLight':
                        lightConfig.addSpotLight();
                        lightConfig.addSpotLightHelper();
                        showLightParamDebug(lightConfig.spotLight);
                        break
                    case 'HemisphereLight':
                        lightConfig.addHemisphereLight();
                        lightConfig.addHemisphereLightHelper();
                        showLightParamDebug(lightConfig.hemisphereLight);
                        break
                    case 'DebugHelper':
                        if (scene.contains(lightConfig.directionalLight)) {
                            lightConfig.addDirectionLightHelper();
                        }
                        if (scene.contains(lightConfig.pointLight)) {
                            lightConfig.addPointLightHelper();
                        }
                        if (scene.contains(lightConfig.hemisphereLight)) {
                            lightConfig.addHemisphereLightHelper();
                        }
                        if (scene.contains(lightConfig.spotLight)) {
                            lightConfig.addSpotLight();
                        }

                        break;
                    default:
                        console.error("error " + switchFlag + "   " + checkValue);
                }
            }
            else {
                switch (switchFlag) {
                    case 'AmbientLight':
                        scene.remove(lightConfig.ambientLight);
                        break;
                    case 'DirectionLight':
                        scene.remove(lightConfig.directionalLight);
                        scene.remove(lightConfig.directionalLightHelper);
                        break;
                    case 'PointLight':
                        scene.remove(lightConfig.pointLight);
                        scene.remove(lightConfig.pointLightHelper);
                        break;
                    case 'SpotLight':
                        scene.remove(lightConfig.spotLight);
                        scene.remove(lightConfig.spotLightHelper);
                        break
                    case 'HemisphereLight':
                        scene.remove(lightConfig.hemisphereLight);
                        scene.remove(lightConfig.hemisphereLightHelper);
                        break
                    case 'DebugHelper':
                        scene.remove(lightConfig.directionalLightHelper);
                        scene.remove(lightConfig.pointLightHelper);
                        scene.remove(lightConfig.spotLightHelper);
                        scene.remove(lightConfig.hemisphereLightHelper);

                        break;
                    default:
                        console.error("error " + switchFlag + "   " + checkValue);
                        break;


                }
                //destory the gui
                showLightParamDebug(null);
            }
        }
    };
    var data = {
        DebugHelper: false,
        AmbientLight: false,
        DirectionLight: false,
        PointLight: false,
        SpotLight: false,
        HemisphereLight: false
    };
    scene.children.forEach(function (child) {
        if (child instanceof THREE.AmbientLight) {
            data.AmbientLight = true;
        }
        if (child instanceof THREE.DirectionalLight) {
            data.DirectionLight = true;
        }
        if (child instanceof THREE.PointLight) {
            data.PointLight = true;
        }
        if (child instanceof THREE.SpotLight) {
            data.SpotLight = true;
        }
        if (child instanceof THREE.HemisphereLight) {
            data.HemisphereLight = true;
        }
    });

    var gui = new dat.GUI();
    gui.add(data, "DebugHelper").onChange(callback("DebugHelper"));
    gui.add(data, "AmbientLight").onChange(callback("AmbientLight"));
    gui.add(data, "DirectionLight").onChange(callback("DirectionLight"));
    gui.add(data, "PointLight").onChange(callback("PointLight"));
    gui.add(data, "SpotLight").onChange(callback("SpotLight"));
    gui.add(data, "HemisphereLight").onChange(callback("HemisphereLight"));
    gui.open();


    function showLightParamDebug(light) {
        if (this.gui != undefined) {
            this.gui.destroy();
            this.gui = undefined;
        }
        if (light == null) {
            return
        }
        this.gui = new dat.GUI();

        var gui = this.gui;


        var data = {
            "color": 0xffffff,
            "groundColor": 0xffffff
        };

        data.color = light.color.getHex();
        if (light.groundColor != undefined) {
            data.groundColor = light.groundColor.getHex();
        }

        var callback = function (switchFlag) {
            return function (value) {
                switch (switchFlag) {
                    case 'color':
                        console.log("value " + value);
                        if (typeof value === "string") {
                            value = value.replace('#', '0x');
                        }

                        light.color.setHex(value);

                        break;
                    case 'groundColor':
                        if (typeof value === "string") {
                            value = value.replace('#', '0x');
                        }

                        light.groundColor.setHex(value);
                }
                renderer.render(scene, camera);

                light.updateMatrix();

                if (lightConfig.directionalLightHelper) {
                    lightConfig.directionalLightHelper.update();
                }
                if (lightConfig.pointLightHelper) {
                    lightConfig.pointLightHelper.update();
                }
                if (lightConfig.spotLightHelper) {
                    lightConfig.spotLightHelper.update();
                }
                if (lightConfig.hemisphereLightHelper) {
                    lightConfig.hemisphereLightHelper.update();
                }

                console.log(light.distance);
            }
        };

        gui.add(light, "type");
        gui.addColor(data, "color").onChange(callback("color"));
        if (typeof light.receiveShadow == 'undefined') {
            light.receiveShadow = false;
        }
        gui.add(light, "receiveShadow");
        gui.add(light, "intensity", 0, 10).onChange(callback("intensity"));

        //位置和旋转
        DrawGUI.wrapperObject3DEssential.position(light, gui, {callback: callback("Position")});
        DrawGUI.wrapperObject3DEssential.rotation(light, gui, {callback: callback("Rotation")});

        //分别设置
        if (light instanceof THREE.AmbientLight) {
            if (typeof light.castShadow == 'undefined') {
                light.castShadow = false;
            }
            gui.add(light, "castShadow");
        }
        if (light instanceof THREE.DirectionalLight) {
            addShadow(gui);
            //target
            DrawGUI.wrapperObject3DEssential.position(light.target, gui, {
                title: "Target",
                callback: callback("Target")
            });
        }
        if (light instanceof THREE.PointLight) {
            showDistanceAndDecay();
            addShadow(gui);
        }
        if (light instanceof THREE.SpotLight) {

            showDistanceAndDecay();
            gui.add(light, "penumbra", 0, 1).onChange(callback("decay"));
            gui.add(light, "angle", 0, Math.PI / 3).onChange(callback("decay"));
            addShadow(gui);
        }
        if (light instanceof THREE.HemisphereLight) {
            gui.addColor(data, "groundColor", callback("groundColor"));
        }
        gui.open();

        function showDistanceAndDecay() {
            gui.add(light, "distance", 0, 5000).onChange(callback("distance"));
            gui.add(light, "decay", 0, 10).onChange(callback("decay"));
        }

        function addShadow(gui_) {
            var gui = gui_.addFolder("Shadow");
            gui.add(light.shadow, "bias", -5, 5).onChange(callback("bias"));
            gui.add(light.shadow, "radius", 0, 1000).onChange(callback("radius"));
            gui.add(light.shadow.mapSize, "x", 0, 2048).onChange(callback("radius"));
            gui.add(light.shadow.mapSize, "y", 0, 2048).onChange(callback("radius"));
            // DrawGUI.wrapperObject3DEssential.autoShowObject(light.shadow.camera, gui, TestUtil.cameraAjust, function () {
            //     light.shadow.camera.updateProjectionMatrix();
            // });
        }
    }
}

/**
 * Created by scenic on 2017/8/2.
 */
function StartWithKey(parameters) {
    StartWidthRayCast.call(this, parameters);
    var scope = this;


    this.OnKeyListener = new ListenerExecute();
    this.OnKeyUpListener = new ListenerExecute();
    this.OnKeyDownListener = new ListenerExecute();
    this.currentViewport = undefined;


    this.OnMouseMoveListener.addListener(function (event) {
        scope.currentViewport = Utils.getCurrentViewport(event);
    });

    var onKeyDown = (function () {
        var keyArray = [];

        var currentViewport, scene, control, translate;
        var isCmdKey = false;

        function getCmd() {
            var cmd = "";
            for (var i = 0; i < keyArray.length; i++) {
                cmd += keyArray[i];
            }
            keyArray.deleteAll();
            console.log("cmd is " + cmd);
            switch (cmd) {
                case 'camera':
                    new CameraHelper(scope).showCameraHelper(scope.scenes[0]);
                    break;
                case "resource":
                    console.log("show resource");
                    Utils.showSceneResource(scene);
                    break;

                case "cgc":
                    //change camera
                    currentViewport.changeCamera();
                    break;
                case 'top':
                    Utils.topView(scene);
                    break;
                case 'front':
                    Utils.frontView(scene);
                    break;
                case 'left':
                    Utils.leftView(scene);
                    break;
                case "perspective":
                    Utils.perspectiveView(scene);
                    break;
                case 'light':
                    new LightDebugHelper(scene, scope.renderer, scene.viewport.camera, scene.lightConfig);
                    scope.OnRePaintListener.addListener(function () {
                        scene.lightConfig.updateHelper();
                    }, "debug-light-helper");
                    break;
                case 'save':
                    var str = JSON.stringify(translate.object);
                    CommonUtils.doSave(str, translate.object.name + ".txt");
                    break;
                case 'print':
                    console.log(scene);
                    console.log(currentViewport);
                    console.log(scope);
                    break;

                default:
                    console.log(cmd);
                    break;

            }
        }


        return function (event) {
            scope.OnKeyListener.forEach(function (l) {
                l.call(scope, event, "keyDown");
            });
            scope.OnKeyDownListener.forEach(function (l) {
                l.call(scope, event);
            });

            console.log(event);

            if (!scope.currentViewport) {
                return;
            }

            currentViewport = scope.currentViewport;
            scene = currentViewport.associateScene;
            control = currentViewport.control;
            translate = scene.translate;


            switch (event.key) {
                case 'Escape':
                    keyArray.deleteAll();
                    isCmdKey = false;
                    break;
                case ' ':
                case 'Space':
                    if (isCmdKey) {
                        getCmd();
                    } else {
                        console.log("Enter the cmd");
                        isCmdKey = true;
                    }
                    break;
                case 'Enter':
                    if (isCmdKey) {
                        getCmd();
                    } else {
                        console.log("Enter the cmd");
                        isCmdKey = true;
                    }
                    break;
                case 'Delete':
                    var parent = translate.object.parent;
                    parent.remove(translate.object);
                    scene.detachTranslate();

                    break;


                default:
                    if (isCmdKey) {
                        keyArray.push(event.key);
                        console.log(keyArray);
                    }

                    break;
            }


            if (!isCmdKey) {
                switch (event.key) {
                    case 'm':

                        if (this.edgesShow == undefined) {
                            this.edgesShow = false;
                        }
                        this.edgesShow = !this.edgesShow;
                        Utils.showEdgesHelper(scene, scope, this.edgesShow);
                        break;
                    case 'M':
                        if (this.wireframe == undefined) {
                            this.wireframe = false;
                        }

                        scene.traverse(function (obj) {
                            if (obj instanceof THREE.Mesh) {
                                obj.material.wireframe = !obj.material.wireframe;
                                if (obj.edgesHelper) {
                                    obj.edgesHelper.visible = !obj.material.wireframe;
                                }
                            }
                        });

                        break;
                    case 'g':
                        //G 显示栅格
                        scene.updateGridHelper();
                        scene.updateAxisHelper();
                        break;

                    case 't':
                        Utils.topView(scene);
                        break;
                    case 'f':
                        Utils.frontView(scene);
                        break;
                    case 'l':
                        Utils.leftView(scene);
                        break;
                    case "p":
                        Utils.perspectiveView(scene);
                        break;
                    case 'c':
                        var translateObject = translate.object;
                        if (translateObject) {
                            control.target.setFromMatrixPosition(translateObject.matrixWorld);
                            control.update();
                        }
                        break;
                    case 'd':
                        var image = CommonUtils.screenCapture(scope).replace("image/png", "image/octet-stream");
                        window.location.href = image; // it will save locally
                        break;
                    case 's':
                        console.log(translate.object);
                        break;
                    case 'v':
                        var translateObject = translate.object;
                        if (translateObject) {
                            translateObject.visible = !translateObject.visible;
                            if (translateObject.edgesHelper) {
                                translateObject.edgesHelper.visible = translateObject.visible;
                            }
                        }
                        break;
                }

                switch (event.keyCode) {
                    case 81: // Q
                        translate.setSpace(translate.space === "local" ? "world" : "local");
                        console.log("space is " + translate.space);
                        break;

                    case 17: // Ctrl
                        if (translate.translationSnap == null) {
                            translate.setTranslationSnap(10);
                            translate.setRotationSnap(THREE.Math.degToRad(15));
                        } else {
                            translate.setTranslationSnap(null);
                            translate.setRotationSnap(THREE.Math.degToRad(15));
                        }

                        break;

                    case 87: // W
                        translate.setMode("translate");
                        translate.model = "translate";
                        break;

                    case 69: // E
                        translate.setMode("rotate");
                        translate.model = "rotate";
                        break;

                    case 82: // R
                        translate.setMode("scale");
                        translate.model = "scale";
                        break;

                    case 187:
                    case 107: // +, =, num+
                        translate.setSize(translate.size + 0.1);
                        break;

                    case 189:
                    case 109: // -, _, num-
                        translate.setSize(Math.max(translate.size - 0.1, 0.1));
                        break;

                }

            }
        }
    })();

    document.addEventListener('keydown', onKeyDown, false);
    document.addEventListener('keyup', function (event) {
        scope.OnKeyListener.forEach(function (l) {
            l.call(scope, event, "keyUp");
        });
        scope.OnKeyUpListener.forEach(function (l) {
            l.call(scope, event);
        });
    }, false);
}

StartWithKey.prototype = Object.assign(Object.create(StartWidthRayCast.prototype), {
    constructor: StartWithKey
});

/**
 * Created by scenic on 2017/8/2.
 */
function StartNB(parameters) {
    StartWithKey.call(this, parameters);
    var scope = this;
    this.helperHightLight = new HighlightHelper(this);
    this.OnDoubleClickListener.addListener(function (event) {
        var view = Utils.getCurrentViewport(event);
        if (event.button == 0) {
            var scene = view.associateScene;
            var intersects = scope.getRayCastIntersects(event);
            if (intersects.length > 0) {
                scene.attachTranslate(intersects[0].object);
            } else {
                scene.detachTranslate();
            }
        }
    }, "attach");

    this.OnMouseWheelListener.addListener(function (event) {
        var view = Utils.getCurrentViewport(event);
        view.associateScene.translate.update();
    }, "updateTranslate");

    function memoryTest(scene, camera) {
        var info = scope.renderer.info;
        var programs = info.programs.length;
        var geometries = info.memory.geometries;
        var textures = info.memory.textures;
        var vertices = info.render.vertices;
        var points = info.render.points;
        var faces = info.render.faces;
        var calls = info.render.calls;
        var frame = info.render.frame;
        var element = scene.viewport.domElement;

        this.draw2DClear(element);

        //build message
        var message = [
            "programs length:" + programs,
            "scenic length : " + scene.children.length,
            "geometries:" + geometries,
            "textures:" + textures,
            "vertices:" + vertices,
            "points:" + points,
            "faces:" + faces,
            "calls:" + calls,
            "frame:" + frame,
            "positionX:" + scope.mousePosition.x,
            "positionX:" + scope.mousePosition.y,
        ];
        if (scene.gridHelper) {
            message.push("grid size:　" + scene.gridHelper.size / scene.gridHelper.divisions);
        }
        //finish build message


        var row = 5;
        var baseY = 20;
        var baseX = 20;
        for (var i = 0; i < message.length; i++) {
            var msg = message[i];
            var offsetX = parseInt(i / row) * 180;
            var offsetY = parseInt(i % row) * 25;
            this.draw2DText(element, msg, baseX + offsetX, baseY + offsetY);
        }


    }

    this.OnRePaintListener.addListener(memoryTest, "memory-test");


    this.OnRePaintListener.addListener(function (scene, camera) {
        //我们规定，所有mesh的EdgesHelper的对象对放在scene的一层极中，这样可以减少搜索所耗费的时间
        var children = scene.children;
        for (var i = 0; i < children.length; i++) {
            var helper = children[i];
            if (helper instanceof EdgesHelper && helper.visible) {
                var mesh = helper.associateMesh;
                helper.synchronizeTranslate(mesh);
            }
        }

        if (scene.translate && scene.translate.object) {
            scene.translate.update();
        }
    }, "update-mesh-edges-helper");

}

StartNB.prototype = Object.assign(Object.create(StartWithKey.prototype), {
    constructor: StartNB,
    dispose: function () {
        StartWithKey.prototype.dispose.call(this);


    },
    selectedObject: function (object) {
        this.helperHightLight.attachSelected(object);
    }
});

/**
 * Created by scenic on 2017/8/2.
 */
function StartEffectComposer(parameters) {
    StartNB.call(this, parameters);
    this.composer = new THREE.EffectComposer(this.renderer);
    this.renderPass = new THREE.RenderPass();
    this.outlinePass = new THREE.OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight));
    this.effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
    this.effectFXAA.renderToScreen = true;
    this.composer.addPass(this.renderPass);
    this.composer.addPass(this.effectFXAA);
    this.composer.addPass(this.outlinePass);
}

StartEffectComposer.prototype = Object.assign(Object.create(StartNB.prototype), {
    constructor: StartEffectComposer,
    render: function (scene, camera) {
        StartNB.prototype.render.call(this, scene, camera);
        this.renderPass.scene = scene;
        this.renderPass.camera = camera;
        this.outlinePass.renderScene = scene;
        this.outlinePass.renderCamera = camera;
        this.effectFXAA.uniforms['resolution'].value.set(1 / scene.viewport.width, 1 / scene.viewport.height);
        this.outlinePass.resolution = scene.viewport.width / scene.viewport.height;
        if (scene.translate.object) {
            this.outlinePass.selectedObjects[0] = scene.translate.object;
        }

        this.composer.render();
    }
});

/**
 * Created by scenic on 2017/8/2.
 */

function LightConfig(_scene) {
    this.ambientLight = undefined;
    this.directionalLight = undefined;
    this.hemisphereLight = undefined;
    this.pointLight = undefined;
    this.spotLight = undefined;

    this.directionalLightHelper = undefined;
    this.hemisphereLightHelper = undefined;
    this.pointLightHelper = undefined;
    this.spotLightHelper = undefined;

    this.scene = _scene;
    this.scene.lightConfig = this;
}

LightConfig.prototype = Object.assign({
    internalAddToScene: function (object) {
        if (!this.scene.contains(object)) {
            this.scene.add(object);
        }
    },
    addAmbientLight: function () {
        if (this.ambientLight == undefined) {
            var color = "#49494c";
            var intensity = 1;
            var light = new THREE.AmbientLight(color, intensity);
            light.name = "AmbientLight";
            this.ambientLight = light;

        }
        this.internalAddToScene(this.ambientLight);
        return this;
    },

    addDirectionLight: function () {
        if (this.directionalLight == undefined) {
            var color = 0xffffff;
            var intensity = 1.5;
            var light = new THREE.DirectionalLight(color, intensity);
            light.name = "DirectionLight";
            light.position.set(0, 200, 3000);
            light.target.position.y = 300;
            light.castShadow = true;
            light.shadow.mapSize.set(2048, 2048);

            var d = 900;
            light.shadow.bias = -0.003;
            light.shadow.camera.left = -d;
            light.shadow.camera.right = d;
            light.shadow.camera.top = d;
            light.shadow.camera.bottom = -d;
            light.shadow.camera.far = 3000;
            this.directionalLight = light;
        }
        this.internalAddToScene(this.directionalLight);

        return this;
    },
    addDirectionLightHelper: function () {
        if (this.directionalLightHelper == undefined) {
            this.directionalLightHelper = new THREE.DirectionalLightHelper(this.directionalLight, 200);
            this.directionalLightHelper.name = "directionalLightHelper";
        }
        this.internalAddToScene(this.directionalLightHelper);
        this.directionalLightHelper.update();
    },

    addPointLight: function () {
        if (this.pointLight == undefined) {
            var color = 0xffffff;
            var intensity = 1;
            var distance = 100;
            var decay = 0.1;
            var light = new THREE.PointLight(color, intensity, distance, decay);
            light.shadow.camera.near = 1;
            light.shadow.camera.Far = 30000;
            light.shadow.bias = 0.01;
            light.castShadow = true;
            light.position.set(0, 800, 0);
            this.pointLight = light;
        }
        this.internalAddToScene(this.pointLight);
        return this;
    },
    addPointLightHelper: function () {
        if (this.pointLightHelper == undefined) {
            this.pointLightHelper = new THREE.PointLightHelper(this.pointLight, 100);
        }
        this.internalAddToScene(this.pointLightHelper);
        this.pointLightHelper.update();
    },

    addSpotLight: function () {
        if (this.spotLight == undefined) {
            var color = 0xffffff;
            var intensity = 1;
            var distance = 1000;
            var angle = Math.PI / 4;
            var penumbra = 0.05;
            var decay = 0.5;

            var light = new THREE.SpotLight(color, intensity, distance, angle, penumbra, decay);
            light.position.set(15, 400, 35);
            light.castShadow = true;

            light.shadow.mapSize.width = 1024;
            light.shadow.mapSize.height = 1024;
            light.shadow.camera.near = 1;
            light.shadow.camera.far = 200;

            this.spotLight = light;
        }

        this.internalAddToScene(spotLight);
        return this;
    },
    addSpotLightHelper: function () {
        if (this.spotLightHelper == undefined) {
            this.spotLightHelper = new THREE.SpotLightHelper(this.spotLight);
        }
        this.internalAddToScene(this.spotLightHelper);
        this.spotLightHelper.update();
    },
    addHemisphereLight: function () {
        if (this.hemisphereLight == null) {
            var skyColor = 0xffffff;
            var grandColor = 0x54ff62;
            var intensity = 0.5;
            this.hemisphereLight = new THREE.HemisphereLight(skyColor, grandColor, intensity);

        }
        this.internalAddToScene(this.hemisphereLight);
    },
    addHemisphereLightHelper: function () {
        if (this.hemisphereLightHelper == undefined) {
            this.hemisphereLightHelper = new THREE.HemisphereLightHelper(this.hemisphereLight, 200);
        }
        this.internalAddToScene(this.hemisphereLightHelper);
        this.hemisphereLightHelper.update();
    },

    removeAllHelper: function () {
        this.scene.remove(this.directionalLightHelper);
        this.scene.remove(this.pointLightHelper);
        this.scene.remove(this.spotLightHelper);
        this.scene.remove(this.hemisphereLightHelper);
        this.directionalLightHelper = this.pointLightHelper = this.spotLightHelper = this.hemisphereLightHelper = undefined;
    },

    updateHelper: function () {
        if (this.directionalLightHelper) {
            this.directionalLightHelper.update();
        }

        if (this.pointLightHelper) {
            this.pointLightHelper.update();
        }

        if (this.spotLightHelper) {
            this.spotLightHelper.update();
        }

        if (this.hemisphereLightHelper) {
            this.hemisphereLightHelper.update();
        }
    }
});

/**
 * Created by scenic on 2017/8/2.
 */
function DebugMaterialHelper() {
    this.gui = undefined;
    this.object = undefined;
    this.material = undefined;
    var scope = this;

    function whichMaterial(gui) {

        var material = scope.material;
        if (material instanceof THREE.MeshBasicMaterial) {
            guiMeshBasicMaterial(gui);
        }
        else if (material instanceof THREE.MeshPhongMaterial) {
            guiMeshPhongMaterial(gui);
        }
        else if (material instanceof THREE.MeshLambertMaterial) {
            guiMeshLambertMaterial(gui);
        }
        else if (material instanceof THREE.MeshDepthMaterial) {
            guiMeshDepthMaterial(gui);
        }
        else if (material instanceof THREE.MeshNormalMaterial) {
            guiMeshNormalMaterial(gui);
        }
        else if (material instanceof THREE.MeshFaceMaterial) {

        }
        else if (material instanceof THREE.MeshStandardMaterial) {
            guiMeshStandardMaterial(gui);
        }
        else if (material instanceof THREE.LineBasicMaterial) {
            guiLineBasicMaterial(gui);
        }
        else if (material instanceof THREE.MultiMaterial) {

        }
        else {
            console.error("can not matcher the material");
        }
    }

    function guiMeshPhongMaterial(gui) {
        var material = scope.material;
        var geometry = scope.object.geometry;
        // var textureMapKeys = getObjectsKeys(textureMaps);
        var data = {
            color: material.color.getHex(),
            emissive: material.emissive.getHex(),
            // 镜子似的
            specular: material.specular.getHex(),
            // lightMap: textureMapKeys,
        };

        var folder = gui.addFolder('THREE.MeshPhongMaterial');

        folder.addColor(data, 'emissive').listen().onChange(handleColorChange(material.emissive));
        folder.addColor(data, 'specular').listen().onChange(handleColorChange(material.specular));

        //shininess 反射
        folder.add(material, 'shininess', 0, 100);
        folder.add(material, 'shading', ['FlatShading', 'SmoothShading']).onChange(function (value) {
            switch (value) {
                case 'FlatShading':
                    material.shading = THREE.FlatShading;
                    break;
                case 'SmoothShading':
                    material.shading = THREE.SmoothShading;
                    break;

            }

            updateNeed(material, geometry);
        });
        guiMaterialCommonOther(folder, material);
        guiMaterialCommon(folder, material);
        folder.add(material, 'vertexColors', ['NoColors', 'FaceColors', 'VertexColors']).onChange(function (value) {
            switch (value) {
                case 'NoColors':
                    material.vertexColors = THREE.NoColors;
                    break;
                case 'FaceColors':
                    material.vertexColors = THREE.FaceColors;
                    break;
                case 'VertexColors':
                    material.vertexColors = THREE.VertexColors;
                    break;
            }
        });

        // folder.add(data, 'lightMap', textureMapKeys).onChange(updateTexture(material, 'lightMap', textureMaps));
        folder.open();


    }

    function getObjectsKeys(obj) {
        var keys = [];
        for (var key in obj) {

            if (obj.hasOwnProperty(key)) {

                keys.push(key);

            }

        }
        return keys;
    }

    function handleColorChange(color) {
        return function (value) {
            if (typeof value === "string") {
                value = value.replace('#', '0x');
            }
            color.setHex(value);
        };
    }

    function updateMorphs(torus, material) {

        return function () {

            torus.updateMorphTargets();
            material.needsUpdate = true;

        };

    }

    function needsUpdate(material, geometry) {

        return function () {

            material.shading = +material.shading; //Ensure number
            material.vertexColors = +material.vertexColors; //Ensure number
            material.side = +material.side; //Ensure number
            material.needsUpdate = true;
            geometry.verticesNeedUpdate = true;
            geometry.normalsNeedUpdate = true;
            geometry.colorsNeedUpdate = true;

        };

    }

    function updateNeed(material, geometry) {
        material.shading = +material.shading; //Ensure number
        material.vertexColors = +material.vertexColors; //Ensure number
        material.side = +material.side; //Ensure number
        material.needsUpdate = true;
        geometry.verticesNeedUpdate = true;
        geometry.normalsNeedUpdate = true;
        geometry.colorsNeedUpdate = true;
    }

    function guiMaterialCommon(gui, material) {
        gui.add(material, 'wireframe');
        gui.add(material, 'wireframeLinewidth', 0, 10);
    }

    function guiMaterialCommonOther(gui) {
        var material = scope.material;

        // var envMapKeys = getObjectsKeys(envMaps);
        // var textureMapKeys = getObjectsKeys(textureMaps);

        var data = {
            color: material.color.getHex(),
            // envMaps: envMapKeys,
            // map: textureMapKeys,
            // specularMap: textureMapKeys,
            // alphaMap: textureMapKeys
        };


        gui.addColor(data, 'color').listen().onChange(handleColorChange(material.color));
        gui.add(material, 'fog');

        // gui.add(data, 'envMaps', envMapKeys).onChange(updateTexture(material, 'envMap', envMaps));
        // gui.add(data, 'map', textureMapKeys).onChange(updateTexture(material, 'map', textureMaps));
        // gui.add(data, 'specularMap', textureMapKeys).onChange(updateTexture(material, 'specularMap', textureMaps));
        // gui.add(data, 'alphaMap', textureMapKeys).onChange(updateTexture(material, 'alphaMap', textureMaps));

    }

    function guiMeshBasicMaterial(gui) {
        if (selectedObject != null) {
            var mesh = selectedObject;
            var material = selectedObject.material;
            var geometry = selectedObject.geometry;

            var folder = gui.addFolder('THREE.MeshBasicMaterial');

            guiMaterialCommonOther(folder);
            guiMaterialCommon(folder, material);

            folder.add(material, 'shading', constants.shading);
            folder.add(material, 'vertexColors', constants.colors).onChange(needsUpdate(material, geometry));
            folder.add(material, 'morphTargets').onChange(updateMorphs(mesh, material));
            folder.add(material, 'combine', constants.combine).onChange(updateMorphs(mesh, material));
            folder.add(material, 'reflectivity', 0, 1);
            folder.add(material, 'refractionRatio', 0, 1);

            folder.open();
        }
    }

    function guiMeshLambertMaterial(gui) {
        var object = scope.object;
        var mesh = object;
        var material = scope.material;
        var geometry = object.geometry;
        // var envMapKeys = getObjectsKeys(envMaps);
        // var textureMapKeys = getObjectsKeys(textureMaps);

        var data = {
            color: material.color.getHex(),
            emissive: material.emissive.getHex(),
            lightMapIntensity: material.lightMapIntensity,
            emissiveIntensity: material.emissiveIntensity,
            aoMapIntensity: material.aoMapIntensity,
            wireframe: material.wireframe,
            wireframeLinewidth: material.wireframeLinewidth,
            skinning: material.skinning,
            morphTargets: material.morphTargets,
            morphNormals: material.morphNormals,
            combine: 'MultiplyOperation',

            // envMaps: envMapKeys,
            // map: textureMapKeys,
            // specularMap: textureMapKeys,
            // alphaMap: textureMapKeys
        };


        var folder = gui.addFolder('THREE.MeshLambertMaterial');

        folder.addColor(data, 'emissive').onChange(handleColorChange(material.emissive));
        folder.addColor(data, 'color').onChange(handleColorChange(material.emissive));
        folder.add(data, 'lightMapIntensity', 0, 2).onChange(function (value) {
            material.lightMapIntensity = value;
        });
        folder.add(data, 'emissiveIntensity', 0, 2).onChange(function (value) {
            material.lightMapIntensity = value;
        });
        folder.add(data, 'aoMapIntensity', 0, 2).onChange(function (value) {
            material.lightMapIntensity = value;
        });

        folder.add(data, 'wireframe').onChange(function (value) {
            material.wireframe = value;
        });
        folder.add(data, 'wireframeLinewidth', 1, 10).onChange(function (value) {
            material.wireframeLinewidth = value;
        });
        folder.add(data, 'skinning').onChange(function (value) {
            material.skinning = value;
        });
        folder.add(data, 'morphTargets').onChange(function (value) {
            material.morphTargets = value;
        });
        folder.add(data, 'morphNormals').onChange(function (value) {
            material.morphNormals = value;
        });
        folder.add(data, 'combine', ['MultiplyOperation', 'MixOperation', 'AddOperation']).onChange(function (value) {
            switch (value) {
                case 'MultiplyOperation':
                    material.combine = THREE.MultiplyOperation;
                    break;
                case 'MixOperation':
                    material.combine = THREE.MixOperation;
                    break;
                case 'AddOperation':
                    material.combine = THREE.AddOperation;
                    break;
            }

        });


        folder.open();
        // guiMaterialCommonOther(folder);
        //
        // guiMaterialCommon(folder, material);
        // folder.add(material, 'vertexColors', constants.colors).onChange(needsUpdate(material, geometry));
        // ß
        // folder.add(material, 'morphTargets').onChange(updateMorphs(mesh, material));
        // folder.add(material, 'combine', constants.combine).onChange(updateMorphs(mesh, material));
        // folder.add(material, 'reflectivity', 0, 1);
        // folder.add(material, 'refractionRatio', 0, 1);
        //folder.add( material, 'skinning' );

    }

    function guiMeshStandardMaterial(gui) {

        var mesh = scope.object;
        var materialInternal = scope.material;
        var geometryInternal = scope.object.geometry;

        var data = {
            color: materialInternal.color.getHex(),
            emissive: materialInternal.emissive.getHex(),
            // envMaps : envMapKeys,
            //map : textureMapKeys,
            // lightMap : textureMapKeys,
            // specularMap : textureMapKeys,
            // alphaMap : textureMapKeys
        };

        var folder = gui.addFolder('THREE.MeshStandardMaterial');
        folder.open();
        folder.addColor(data, 'color').onChange(handleColorChange(materialInternal.color));
        folder.addColor(data, 'emissive').onChange(handleColorChange(materialInternal.emissive));

        folder.add(materialInternal, 'roughness', 0, 1);
        folder.add(materialInternal, 'metalness', 0, 1);
        folder.add(materialInternal, 'shading', ['FlatShading', 'SmoothShading']).onChange(function (value) {
            switch (value) {
                case 'FlatShading':
                    materialInternal.shading = THREE.FlatShading;
                    break;
                case 'SmoothShading':
                    materialInternal.shading = THREE.SmoothShading;
                    break;

            }

            updateNeed(materialInternal, geometryInternal);
        });
        folder.add(materialInternal, 'wireframe');
        folder.add(materialInternal, 'wireframeLinewidth', 0, 10);
        folder.add(materialInternal, 'vertexColors', ['NoColors', 'FaceColors', 'VertexColors']).onChange(function (value) {
            switch (value) {
                case 'NoColors':
                    materialInternal.vertexColors = THREE.NoColors;
                    break;
                case 'FaceColors':
                    materialInternal.vertexColors = THREE.FaceColors;
                    break;
                case 'VertexColors':
                    materialInternal.vertexColors = THREE.VertexColors;
                    break;
            }
        });
        folder.add(materialInternal, 'fog');
        // folder.add( data, 'envMaps', envMapKeys ).onChange( updateTexture( materialInternal, 'envMap', envMaps ) );
        // folder.add( data, 'map', textureMapKeys ).onChange( updateTexture( materialInternal, 'map', textureMaps ) );
        // folder.add( data, 'lightMap', textureMapKeys ).onChange( updateTexture( materialInternal, 'lightMap', textureMaps ) );
        // folder.add( data, 'alphaMap', textureMapKeys ).onChange( updateTexture( materialInternal, 'alphaMap', textureMaps ) );

        // TODO roughnessMap and metalnessMap


    }

    function guiMeshDepthMaterial(gui) {
        if (selectedObject != null) {
            var mesh = selectedObject;
            var material = selectedObject.material;
            var geometry = selectedObject.geometry;
            var envMapKeys = getObjectsKeys(envMaps);
            var textureMapKeys = getObjectsKeys(textureMaps);

            var folder = gui.addFolder('THREE.MeshDepthMaterial');

            guiMaterialCommon(folder, material);
            folder.add(material, 'morphTargets').onChange(updateMorphs(mesh, material));
        }
    }

    function guiMeshNormalMaterial(gui) {
        if (selectedObject != null) {
            var mesh = selectedObject;
            var material = selectedObject.material;
            var geometry = selectedObject.geometry;
            var envMapKeys = getObjectsKeys(envMaps);
            var textureMapKeys = getObjectsKeys(textureMaps);

            var folder = gui.addFolder('THREE.MeshNormalMaterial');

            guiMaterialCommon(folder, material);
            folder.add(material, 'morphTargets').onChange(updateMorphs(mesh, material));
        }
    }

    function guiLineBasicMaterial(gui) {

        if (selectedObject != null) {
            var mesh = selectedObject;
            var material = selectedObject.material;
            var geometry = selectedObject.geometry;
            var envMapKeys = getObjectsKeys(envMaps);
            var textureMapKeys = getObjectsKeys(textureMaps);

            var data = {
                color: material.color.getHex()
            };

            var folder = gui.addFolder('THREE.LineBasicMaterial');

            folder.addColor(data, 'color').onChange(handleColorChange(material.color));
            folder.add(material, 'linewidth', 0, 10);
            folder.add(material, 'linecap', ["butt", "round", "square"]);
            folder.add(material, 'linejoin', ["round", "bevel", "miter"]);
            folder.add(material, 'vertexColors', constants.colors).onChange(needsUpdate(material, geometry));
            folder.add(material, 'fog');
        }
    }

    this.attach = function (object, material) {
        this.object = object;
        this.material = material;
        this.gui = new dat.GUI();
        whichMaterial(this.gui);
    };
}

/**
 * Created by scenic on 2017/8/2.
 */
function ShowFaceHelper(start) {
    var rayCast = new THREE.Raycaster();

    var geometry = new THREE.BufferGeometry();
    geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(4 * 3), 3));
    var material = new THREE.LineBasicMaterial({color: 0xff0000, linewidth: 2, transparent: false});
    var line = new THREE.Line(geometry, material);
    this.attach = function () {
        var arg = arguments;
        start.OnRePaintListener.addListener(function (scene, camera) {
            if (!scene.contains(line)) {
                scene.add(line);
            }
            for (var i = 0; i < arg.length; i++) {
                var mesh = arg[i];
                if (mesh) {
                    rayCast.setFromCamera(this.mousePosition, camera);
                    var intersects = rayCast.intersectObject(mesh);
                    if (intersects.length > 0) {

                        var intersect = intersects[0];
                        var intersectFace = intersect.face;

                        var msg = "intersectFace.faceIndex : " + intersect.faceIndex + "<br/>";
                        msg += "intersectFace.normal " + CommonUtils.object2String(intersect.normal) + "<br/>";
                        msg += "a,b,c is : " + intersectFace.a + "," + intersectFace.b + "," + intersectFace.c + "<br/>";

                        if (mesh.geometry.isBufferGeometry) {
                            var position = mesh.geometry.attributes.position;
                            var va = new THREE.Vector3().fromBufferAttribute(position, intersectFace.a);
                            var vb = new THREE.Vector3().fromBufferAttribute(position, intersectFace.b);
                            var vc = new THREE.Vector3().fromBufferAttribute(position, intersectFace.c);
                            msg += "a: " + CommonUtils.object2String(va) + "<br/>";
                            msg += "b: " + CommonUtils.object2String(vb) + "<br/>";
                            msg += "c: " + CommonUtils.object2String(vc) + "<br/>";
                        } else {
                            var va = mesh.geometry.vertices[intersectFace.a];
                            var vb = mesh.geometry.vertices[intersectFace.b];
                            var vc = mesh.geometry.vertices[intersectFace.c];
                            msg += "a: " + CommonUtils.object2String(va) + "<br/>";
                            msg += "b: " + CommonUtils.object2String(vb) + "<br/>";
                            msg += "c: " + CommonUtils.object2String(vc) + "<br/>";
                        }
                        console.log(mesh.geometry);
                        CommonUtils.showDialog(msg);


                        var linePosition = line.geometry.attributes.position;
                        if (mesh.geometry instanceof THREE.BufferGeometry) {
                            var meshPosition = mesh.geometry.attributes.position;
                            linePosition.copyAt(0, meshPosition, intersectFace.a);
                            linePosition.copyAt(1, meshPosition, intersectFace.b);
                            linePosition.copyAt(2, meshPosition, intersectFace.c);
                            linePosition.copyAt(3, meshPosition, intersectFace.a);
                        } else if (mesh.geometry instanceof THREE.Geometry) {
                            // var faces = mesh.geometry.faces;
                            var array = linePosition.array;
                            var vertices = mesh.geometry.vertices;
                            var index = 0;

                            array[index++] = vertices[intersectFace.a].x;
                            array[index++] = vertices[intersectFace.a].y;
                            array[index++] = vertices[intersectFace.a].z;

                            array[index++] = vertices[intersectFace.b].x;
                            array[index++] = vertices[intersectFace.b].y;
                            array[index++] = vertices[intersectFace.b].z;

                            array[index++] = vertices[intersectFace.c].x;
                            array[index++] = vertices[intersectFace.c].y;
                            array[index++] = vertices[intersectFace.c].z;

                            array[index++] = vertices[intersectFace.a].x;
                            array[index++] = vertices[intersectFace.a].y;
                            array[index++] = vertices[intersectFace.a].z;
                        }
                        mesh.updateMatrix();
                        line.geometry.applyMatrix(mesh.matrix);
                        line.visible = true;
                    } else {
                        // line.visible = false;
                    }
                }
            }
        }, "show-face");
    };
}

/**
 * Created by scenic on 2017/8/2.
 */
// export {ListenerExecute} from './core/Base'

exports.Mesh = Mesh;
exports.Scene = Scene;
exports.Viewport = Viewport$1;
exports.Start = Start;
exports.StartWidthRayCast = StartWidthRayCast;
exports.StartWithKey = StartWithKey;
exports.StartNB = StartNB;
exports.StartEffectComposer = StartEffectComposer;
exports.LightConfig = LightConfig;
exports.CameraHelper = CameraHelper;
exports.DebugMaterialHelper = DebugMaterialHelper;
exports.EdgesHelper = EdgesHelper;
exports.HighlightHelper = HighlightHelper;
exports.LightDebugHelper = LightDebugHelper;
exports.ShowFaceHelper = ShowFaceHelper;
exports.File = File;
exports.Folder = Folder;
exports.Utils = Utils;
exports.MathUtils = MathUtils;
exports.GeometryUtils = GeometryUtils;
exports.DebugDraw = DebugDraw;
exports.CommonUtils = CommonUtils$1;
exports.Config = Config;

Object.defineProperty(exports, '__esModule', { value: true });

})));
